参考:
abp官网的文档:微服务解决方案示例 (注意,要配合文档去运行项目,然后反复、认真看文档,文档中已经把项目的细节讲得足够清楚的了,包含:项目引用、依赖、远程调用过程、通信、网关等等)
有大佬为abp的微服务项目进行了讲解和扩展:[Abp vNext微服务实践] - 文章目录----一曲肝腸斷录
下载ABP全部示例代码
ABP示例github下载地址,默认是master,也可以选择最新的版本
ABP微服务架构简略图
问题
MongoDb
问题:BloggingService.Host(博客服务)运行失败,提示MongoDb单台服务器不支持事务,目前我下载的是abp3.2.1版本。说明文档中的MongoDB中提到新版本好像已经解决这个问题,不过微服务示例还没有升级到最新版。
虽然有个服务运行失败,而且我也没安装ElasticSearch、Kibana,但项目还是能够运行起来演示products的效果。
RabbitMQ
问题:目前使用的是3.2.1版本的,发现停止RabbitMQ服务和注释RabbitMQ代码都能正常演示
结论:这个ABP微服务示根本就没有使用RabbitMQ进行消息通信,只不过是有几个项目有引入RabbitMQ模块但是没有使用,微服务示例文档中居然说是使用RabbitMQ通信的,有点误导人。
应该是使用 动态 C# API 客户端 来通信的
项目结构
调用顺序:applications(应用层),项目引用:modules(模块层)、shared(共享层) =》gateways(网关 / 前端后端层) =》microservices(微服务层)
applications(应用层):控制台应用程序 + UI + 身份认证
这些是具有用户界面以与用户交互并使用系统的实际应用程序.
- AuthServer.Host 44399: 托管IdentityServer4以向其他服务和应用程序提供身份验证服务. 它是一个单点登录服务器,包含登录页面.
-
BackendAdminApp.Host 44354: 这是一个后端管理应用程序,用于托管身份和产品管理模块的UI.
-
PublicWebSite.Host 44335: 作为包含简单产品列表页面和博客模块UI的公共网站.
-
ConsoleClientDemo: 一个简单的控制台应用程序,用于演示C#应用程序中使用服务.
gateways(网关 / 前端后端层):控制台应用程序 + 网关 + 通信
网关用于为应用程序提供单一入口点.它还可以用于速率限制,负载平衡等. 使用Ocelot类库.
- BackendAdminAppGateway.Host 44315: 由BackendAdminApp.Host应用程序用作后端.
-
PublicWebSiteGateway.Host 44397: 由PublicWebSite.Host应用程序用作后端.
-
InternalGateway.Host 44329: 用于服务间通信(微服务之间的通信).
microservices(微服务层):控制台应用程序 RabbitMQ + 身份认证 + EFCore
微服务没有UI,但暴露了一些REST API.
- IdentityService.Host 44368: 托管用于管理用户和角色的ABP Identity模块. 它没有其他服务,仅托管Identity模块的API.
-
TenantManagementService.Host 44336: 托管用于管理角色的ABP租户管理模块. 它没有其他服务,仅托管租户管理模块的API.
-
BloggingService.Host 44357: 托管ABP博客模块,该模块用于管理博客和帖子(典型的博客应用程序). 它没有其他服务,仅托管Blogging模块的API.
-
ProductService.Host 44344: 托管用于管理产品的产品模块(位于解决方案内). 它还包含用于创建/更新产品管理数据库架构的EF Core迁移.
modules(模块层):类库 + DDD + DTO + 实体类
项目名称都是带有 Management 字眼的
包含基于DDD原则分层的实际模块:
产品: 使用模块开发最佳实践开发的分层模块. 它可以嵌入到单个应用程序中,也可以通过单独部署API和UI作为微服务托管
- ProductManagement.Domain.Shared: 包含所有层之间共享的常量和类型.
-
ProductManagement.Domain: 包含域逻辑并定义实体,域服务,域事件,业务/域异常.
-
ProductManagement.Application.Contracts: 包含应用程序服务接口和DTO.
-
ProductManagement.Application: 包含应用程序服务的实现.
-
ProductManagement.EntityFrameworkCore: 包含DbContext和其他与EF Core相关的类和配置.
-
ProductManagement.HttpApi: 包含API控制器.
-
ProductManagement.HttpApi.Client: 包含C#代理以远程直接使用HTTP API. 使用ABP的Dynamic C#API客户端功能.
-
ProductManagement.Web: 包含UI元素(页面,脚本,样式..等).
shared(共享层):类库
-
MsDemo.Shared:多租户
项目引用
从项目结构看项目引用
其他项目都项目引用modules(名称带有Management )、shared(名称带有shared)
modules内的product的项目引用关系
ProductManagement.Domain.Shared:没有项目引用
ProductManagement.Domain:项目引用 ProductManagement.Domain.Shared
ProductManagement.EntityFrameworkCore:项目引用 ProductManagement.Domain
ProductManagement.Application.Contracts:项目引用 ProductManagement.Domain.Shared
ProductManagement.HttpApi.Client:项目引用 ProductManagement.Application.Contracts
ProductManagement.HttpApi:项目引用 ProductManagement.Application.Contracts
ProductManagement.Web:项目引用 ProductManagement.HttpApi
ProductManagement.Application:项目引用 ProductManagement.Domain、ProductManagement.Application.Contracts
通信方式
说明:消息和RabbitMQ
参考:[Abp vNext微服务实践] - 服务通讯 [Abp vNext 源码分析] - 13. 本地事件总线与分布式事件总线 (Rabbit MQ)
动态 C# API 客户端
分布式 Event Bus--里面有说道ABP继承RabbitMQ
所有示例--里面有2个RabbitMQ的示例的
ABP可以动态创建C#API客户端代理来调用您的远程HTTP服务(REST API)。这样,您无需处理HttpClient
其他低级详细信息即可调用远程服务并获得结果。
动态C#代理会自动为您处理以下内容;
- 通过考虑HTTP方法,路由,查询字符串参数,请求有效负载和其他详细信息,将C#方法调用映射到远程服务器HTTP调用。
- 通过将访问令牌添加到HTTP标头来认证HTTP客户端。
- 从JSON序列化和反序列化。
- 处理HTTP API版本控制。
- 将相关性ID,当前租户ID和当前区域性添加到请求中。
- 正确处理服务器发送的错误消息并引发适当的异常。
任何类型的.NET客户端都可以使用此系统来使用您的HTTP API。
模块层的ProductManagement.HttpApi.Client类库中的模块类ProductManagementHttpApiClientModule中
- using Volo.Abp.Http.Client
- 模块依赖typeof(AbpHttpClientModule)
- 使用添加Http客户端代理方法:AddHttpClientProxies(typeof(ProductManagementApplicationContractsModule).Assembly, RemoteServiceName)
应用程序层
AuthServer.Host:登录认证服务
- appsettings.json
- RabbitMQ
- Connections
- EventBus,备注:模块类加载模块的DependsOn(typeof(AbpEventBusRabbitMqModule))时,AbpEventBusRabbitMqModule类会默认加载配置文件中的RabbitMQ:EventBus信息,反编译查看AbpEventBusRabbitMqModule的源码如下:
public class AbpEventBusRabbitMqModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { IConfiguration configuration = context.Services.GetConfiguration(); Configure<AbpRabbitMqEventBusOptions>(configuration.GetSection("RabbitMQ:EventBus")); } public override void OnApplicationInitialization(ApplicationInitializationContext context) { context.ServiceProvider.GetRequiredService<RabbitMqDistributedEventBus>().Initialize(); } }
- RabbitMQ
PublicWebSite.Hos:产品、博客展示服务
- appsettings.json
- 登录服务:AuthServer:44399
- 网关:RemoteServices:44397 PublicWebSiteGateway.Host(由PublicWebSite.Host应用程序用作后端)
- AuthServerHostModule
- options.Scope.Add("PublicWebSiteGateway"); //网关
- options.Scope.Add("ProductService"); //产品微服务
- options.Scope.Add("BloggingService"); //博客微服务
Program类读取配置文件:
- AddJsonFile("appsettings.json")加载配置文件
using System; using System.IO; using System.Linq; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Serilog; using Serilog.Events; using Serilog.Sinks.Elasticsearch; namespace AuthServer.Host { public class Program { public static int Main(string[] args) { //TODO: Temporary: it's not good to read appsettings.json here just to configure logging var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .AddEnvironmentVariables() .Build(); Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) .Enrich.WithProperty("Application", "AuthServer") .Enrich.FromLogContext() .WriteTo.File("Logs/logs.txt") .WriteTo.Elasticsearch( new ElasticsearchSinkOptions(new Uri(configuration["ElasticSearch:Url"])) { AutoRegisterTemplate = true, AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv6, IndexFormat = "msdemo-log-{0:yyyy.MM}" }) .CreateLogger(); try { Log.Information("Starting AuthServer.Host."); CreateHostBuilder(args).Build().Run(); return 0; } catch (Exception ex) { Log.Fatal(ex, "AuthServer.Host terminated unexpectedly!"); return 1; } finally { Log.CloseAndFlush(); } } internal static IHostBuilder CreateHostBuilder(string[] args) => Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }) .UseAutofac() .UseSerilog(); } }
跟踪调试查看configuration=》Providers=》[0]=>Data,发现Data是一个字典类型的集合:System.Collections.Generic.IDictionary<string, string>
网关层
PublicWebSiteGateway.Host:产品、博客网关
- appsettings.json
- 登录服务:AuthServer:44399 登录页
- 路由:ReRoutes
- DownstreamPathTemplate(下游路径模板):productManagement?模块层?还是模板
- DownstreamScheme(下游服务协议:http/https):
- DownstreamHostAndPorts(下游服务地址):指定对应微服务
- UpstreamPathTemplate(上游路径模板):
- UpstreamHttpMethod(上游HTTP方法):[ "Put", "Delete", "Get", "Post" ]
- PublicWebSiteGatewayHostModule模块类
-
//读取appsettings.json文件的登录配置 context.Services.AddAuthentication("Bearer") .AddIdentityServerAuthentication(options => { options.Authority = configuration["AuthServer:Authority"]; options.ApiName = configuration["AuthServer:ApiName"]; options.RequireHttpsMetadata = false; }); //Ocelot网关 context.Services.AddOcelot(context.Services.GetConfiguration()); app.UseOcelot().Wait();
-
微服务层
ProductService.Host:产品微服务
- appsettings.json
- 登录服务:AuthServer:44399 =》登录页
BloggingService.Host:博客微服务
- appsettings.json
- 登录服务:AuthServer:44399 =》登录页
- 网关:RemoteServices:44329 =》InternalGateway.Host : 用于服务间通信(微服务之间的通信)
- 认证:IdentityClients:4439 =》登录页