您现在的位置是:网站首页> 编程资料编程资料

通过lms.samples熟悉lms微服务框架的使用详解_其它综合_

2023-05-27 210人已围观

简介 通过lms.samples熟悉lms微服务框架的使用详解_其它综合_

经过一段时间的开发与测试,终于发布了Lms框架的第一个正式版本(1.0.0版本),并给出了lms框架的样例项目lms.samples。本文通过对lms.samples的介绍,简述如何通过lms框架快速的构建一个微服务的业务框架,并进行应用开发。

lms.samples项目基本介绍

lms.sample项目由三个独立的微服务应用模块组成:account、stock、order和一个网关项目gateway构成。

业务应用模块

每个独立的微服务应用采用模块化设计,主要由如下几部分组成:

  1. 主机(Host): 主要用于托管微服务应用本身,主机通过引用应用服务项目(应用接口的实现),托管微服务应用,通过托管应用服务,在主机启动的过程中,向服务注册中心注册服务路由。
  2. 应用接口层(Application.Contracts): 用于定义应用服务接口,通过应用接口,该微服务模块与其他微服务模块或是网关进行rpc通信的能力。在该项目中,除了定义应用服务接口之前,一般还定义与该应用接口相关的DTO对象。应用接口除了被该微服务应用项目引用,并实现应用服务之前,还可以被网关或是其他微服务模块引用。网关或是其他微服务项目通过应用接口生成的代理与该微服务模块通过rpc进行通信。
  3. 应用服务层(Application): 应用服务是该微服务定义的应用接口的实现。应用服务与DDD传统分层架构的应用层的概念一致。主要负责外部通信与领域层之间的协调。一般地,应用服务进行业务流程控制,但是不包含业务逻辑的实现。
  4. 领域层(Domain): 负责表达业务概念,业务状态信息以及业务规则,是该微服务模块的业务核心。一般地,在该层可以定义聚合根、实体、领域服务等对象。
  5. 领域共享层(Domain.Shared): 该层用于定义与领域对象相关的模型、实体等相关类型。不包含任何业务实现,可以被其他微服务引用。
  6. 数据访问(DataAccess)层: 该层一般用于封装数据访问相关的对象。例如:仓库对象、 SqlHelper、或是ORM相关的类型等。在lms.samples中,通过efcore实现数据的读写操作。

服务聚合与网关

lms框架不允许服务外部与微服务主机直接通信,应用请求必须通过http请求到达网关,网关通过lms提供的中间件解析到服务条目,并通过rpc与集群内部的微服务进行通信。所以,如果服务需要与集群外部进行通信,那么,开发者定义的网关必须要引用各个微服务模块的应用接口层;以及必须要使用lms相关的中间件。

开发环境

  1. .net版本: 5.0.101
  2. lms版本: 1.0.0
  3. IDE: (1) visual studio 最新版 (2) Rider(推荐)

主机与应用托管

主机的创建步骤

通过lms框架创建一个业务模块非常方便,只需要通过如下4个步骤,就可以轻松的创建一个lms应用业务模块。

1.创建项目

创建控制台应用(Console Application)项目,并且引用Silky.Lms.NormHost包。

 dotnet add package Silky.Lms.NormHost --version 1.0.0

2.应用程序入口与主机构建

main方法中,通用.net的主机Host构建并注册lms微服务。在注册lms微服务时,需要指定lms启动的依赖模块。

一般地,如果开发者不需要额外依赖其他模块,也无需在应用启动或停止时执行方法,那么您可以直接指定NormHostModule模块。

 public class Program { public static async Task Main(string[] args) { await CreateHostBuilder(args).Build().RunAsync(); } private static IHostBuilder CreateHostBuilder(string[] args) { return Host.CreateDefaultBuilder(args) .RegisterLmsServices() ; } }

3.配置文件

lms框架支持yml或是json格式作为配置文件。通过appsettings.yml对lms框架进行统一配置,通过appsettings.${Environment}.yml对不同环境变量下的配置项进行设置。

开发者如果直接通过项目的方式启动应用,那么可以通过Properties/launchSettings.jsonenvironmentVariables.DOTNET_ENVIRONMENT环境变量。如果通过docker-compose的方式启动应用,那么可以通过.env设置DOTNET_ENVIRONMENT环境变量。

为保证配置文件有效,开发者需要显式的将配置文件拷贝到项目生成目录下。

4.引用应用服务层和数据访问层

一般地,主机项目需要引用该微服务模块的应用服务层和数据访问层。只有主机引用应用服务层,主机在启动时,才会生成服务条目的路由,并且将服务路由注册到服务注册中心。

一个典型的主机项目文件如下所示:

Exenet5.0AlwaysAlwaysAlways

配置

一般地,一个微服务模块的主机必须要配置:服务注册中心、分布式锁链接、分布式缓存地址、集群rpc通信token、数据库链接地址等。

如果使用docker-compose来启动和调试应用的话,那么,rpc配置节点下的的host和port可以缺省,因为生成的每个容器的都有自己的地址和端口号。

如果直接通过项目的方式启动和调试应用的话,那么,必须要配置rpc节点下的port,每个微服务模块的主机应用有自己的端口号。

lms框架的必要配置如下所示:

 rpc: host: 0.0.0.0 rpcPort: 2201 token: ypjdYOzNd4FwENJiEARMLWwK0v7QUHPW registrycenter: connectionStrings: 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183;127.0.0.1:2184,127.0.0.1:2185,127.0.0.1:2186 # 使用分号;来区分不同的服务注册中心 registryCenterType: Zookeeper distributedCache: redis: isEnabled: true configuration: 127.0.0.1:6379,defaultDatabase=0 lock: lockRedisConnection: 127.0.0.1:6379,defaultDatabase=1 connectionStrings: default: server=127.0.0.1;port=3306;database=account;uid=root;pwd=qwe!P4ss;

应用接口

应用接口定义

一般地,在应用接口层开发者需要安装Silky.Lms.Rpc包。如果该微服务模块还涉及到分布式事务,那么还需要安装Silky.Lms.Transaction.Tcc,当然,您也可以选择在应用接口层安装Silky.Lms.Transaction包,在应用服务层安装Silky.Lms.Transaction.Tcc包。

  1. 开发者只需要在应用接口通过ServiceRouteAttribute特性对应用接口进行直接即可。
  2. Lms约定应用接口应当以IXxxAppService命名,这样,服务条目生成的路由则会以api/xxx形式生成。当然这并不是强制的。
  3. 每个应用接口的方法都对应着一个服务条目,服务条目的Id为: 方法的完全限定名 + 参数名
  4. 您可以在应用接口层对方法的缓存、路由、服务治理、分布式事务进行相关配置。该部分内容请参考官方文档
  5. 网关或是其他模块的微服务项目需要引用服务应用接口项目或是通过nuget的方式安装服务应用接口生成的包。
  6. [Governance(ProhibitExtranet = true)]可以标识一个方法禁止与集群外部进行通信,通过网关也不会生成swagger文档。
  7. 应用接口方法生成的WebApi支持restful API风格。Lms支持通过方法的约定命名生成对应http方法请求的WebApi。您当然开发者也可以通过HttpMethodAttribute特性对某个方法进行注解。

一个典型的应用接口的定义

 ///  /// 账号服务 ///  [ServiceRoute] public interface IAccountAppService { ///  /// 新增账号 ///  /// 账号信息 ///  Task Create(CreateAccountInput input); ///  /// 通过账号名称获取账号 ///  /// 账号名称 ///  [GetCachingIntercept("Account:Name:{0}")] [HttpGet("{name:string}")] Task GetAccountByName([CacheKey(0)] string name); ///  /// 通过Id获取账号信息 ///  /// 账号Id ///  [GetCachingIntercept("Account:Id:{0}")] [HttpGet("{id:long}")] Task GetAccountById([CacheKey(0)] long id); ///  /// 更新账号信息 ///  ///  ///  [UpdateCachingIntercept( "Account:Id:{0}")] Task Update(UpdateAccountInput input); ///  /// 删除账号信息 ///  /// 账号Id ///  [RemoveCachingIntercept("GetAccountOutput","Account:Id:{0}")] [HttpDelete("{id:long}")] Task Delete([CacheKey(0)]long id); ///  /// 订单扣款 ///  ///  ///  [Governance(ProhibitExtranet = true)] [RemoveCachingIntercept("GetAccountOutput","Account:Id:{0}")] [Transaction] Task DeductBalance(DeductBalanceInput input); }

应用服务--应用接口的实现

  1. 应用服务层只需要引用应用服务接口层以及领域服务层,并实现应用接口相关的方法。
  2. 确保该微服务模块的主机引用了该模块的应用服务层,这样主机才能够托管该应用本身。
  3. 应用服务层可以通过引用其他微服务模块的应用接口层项目(或是安装nuget包,取决于开发团队的项目管理方法),与其他微服务模块进行rpc通信。
  4. 应用服务层需要依赖领域服务,通过调用领域服务的相关接口,实现该模块的核心业务逻辑。
  5. DTO到实体对象或是实体对DTO对象的映射关系可以在该层指定映射关系。

一个典型的应用服务的实现如下所示:

 public class AccountAppService : IAccountAppService { private readonly IAccountDomainService _accountDomainService; public AccountAppService(IAccountDomainService accountDomainService) { _accountDomainService = accountDomainService; } public async Task Create(CreateAccountInput input) { var account = input.MapTo(); account = await _accountDomainService.Create(account); return account.MapTo(); } public async Task GetAccountByName(string name) { var account = await _accountDomainService.GetAccountByName(name); return account.MapTo(); } public async Task GetAccountById(long id) { var account = await _accountDomainService.GetAccountById(id); return account.MapTo(); } public async Task Update(UpdateAccountInput input) { var account = await _accountDomainService.Update(input); return account.MapTo(); } public Task Delete(long id) { return _accountDomainService.Delete(id); } [TccTransaction(ConfirmMethod = "DeductBalanceConfirm", CancelMethod = "DeductBalanceCancel")] public async Task DeductBalance(DeductBalanceInput input) { var account = await _accountDomainService.GetAccountById(input.AccountId); if (input.OrderBalance > account.Balance) { throw new BusinessException("账号余额不足"); } return await _accountDomainService.DeductBalance(input, TccMethodType.Try); } public Task DeductBalanceConfirm(DeductBalanceInput input) { return _accountDomainService.DeductBalance(input, TccMethodType.Confirm); } public Task DeductBalanceCancel(DeductBalanceInput input) { return _accountDomainService.DeductBalance(input, TccMethodType.Cancel); } }

领域层--微服务的核心业务实现

  1. 领域层是该微服务模块核心业务处理的模块,一般用于定于聚合根、实体、领域服务、仓储等业务对象。
  2. 领域层引用该微服务模块的应用接口层,方便使用dto对象。
  3. 领域层可以通过引用其他微服务模块的应用接口层项目(或是安装nuget包,取决于开发团队的项目管理方法),与其他微服务模块进行rpc通信。
  4. 领域服务必须要直接或间接继承ITransientDependency接口,这样,该领域服务才会被注入到ioc容器。
  5. lms.samples 项目使用TanvirArjel.EFCore.GenericRepository包实现数据的读写操作。

一个典型的领域服务的实现如下所示:

 public class AccountDomainService : IAccountDomainService { private readonly IRepository _repository; private readonly IDistributedCache _accountCache; public AccountDomainService(IRepository repository, IDistributedCache accountCache) { _repository = repository; _accountCache = accountCache; } public async Task Create(Account account) { var exsitAccountCount = await _repository.GetCountAsync(p => p.Name == account.Name); if (exsitAccountCount
                
                

-六神源码网