ASP.NET Core开源Web应用程序框架ABP

"作为面向服务架构(SOA)的一个变体,微服务是一种将应用程序分解成松散耦合服务的新型架构风格. 通过细粒度的服务和轻量级的协议,微服务提供了更多的模块化,使应用程序更容易理解,开发,测试,并且更容易抵抗架构侵蚀. 它使小型团队能够开发,部署和扩展各自的服务,实现开发的并行化.它还允许通过连续重构形成单个服务的架构. 基于微服务架构可以实现持续交付和部署."

— 维基百科

介绍

ABP框架的主要目标之一就是提供便捷的基础设施来创建微服务解决方案.

此示例演示了一个简单而完整的微服务解决方案;

  • 拥有多个可独立可单独部署的微服务.

  • 多个Web应用程序, 每一个都使用不同的API网关.

  • 使用Ocelot库开发了多个网关 / BFFs (用于前端的后端).

  • 包含使用IdentityServer框架开发的 身份认证服务. 它也是一个带有UI的SSO(单点登陆)应用程序.

  • 多个数据库. 一些微服务有自己的数据库,也有一些服务/应用程序共享同一个数据库(以演示不同的用例).

  • 有不同类型的数据库: SQL Server (与 Entity Framework Core ORM) 和 MongoDB.

  • 有一个控制台应用程序使用身份验证展示使用服务最简单的方法.

  • 使用Redis做分布式缓存.

  • 使用RabbitMQ做服务间的消息传递.

  • 使用 Docker & Kubernates 来部署&运行所有的服务和应用程序.

  • 使用 Elasticsearch & Kibana 来存储和可视化日志 (使用Serilog写日志).

下图显示了该系统:

640?wx_fmt=png

源码

你可以从GitHub仓库获取源码.

状态

此示例仍处于开发阶段,尚未完成.

运行解决方案

您可以从 源代码 或者预先配置好的 docker-compose 文件运行.

使用Docker容器

预先要求

由于所有依赖项都已预先配置, 因此作为Docker容器运行更容易. 你只需要安装最新的docker.

运行容器

  • 克隆或下载 ABP仓库.

  • 在存储库的samples/MicroserviceDemo文件夹中打开命令行.

  • 从Docker Hub中拉取image:

    docker-compose -f docker-compose.yml -f docker-compose.migrations.yml pull
  • 如果要在本地构建映像, 可以跳过上述步骤, 使用build命令:

    docker-compose -f docker-compose.yml -f docker-compose.migrations.yml build

    根据你的电脑配置, 构建image可能需要很长时间.

  • 还原 SQL Server 数据库:

    docker-compose -f docker-compose.yml -f docker-compose.migrations.yml run restore-database
  • 启动容器:

    docker-compose up -d
  • 将此行添加到hosts文件的末尾:

    127.0.0.1	auth-server

    hosts文件位于Windows上的C:\Windows\System32\Drivers\etc\hosts文件夹, Linux/MacOS的/etc/hosts中.

运行应用程序

你可能想要了解容器中运行一些应用程序:

  • 后端管理应用程序 (BackendAdminApp.Host): http://localhost:51512 (用于管理系统中的用户和产品)

  • 公共网站 (PublicWebsite.Host): http://localhost:51513 (用于列出产品并运行/管理博客模块)

  • 认证服务器 (AuthServer.Host): http://auth-server:51511/ (用作使用IdentityServer4构建的单点登录和身份验证服务器)

  • Kibana UI: http://localhost:51510 (用于显示/跟踪所有服务/应用程序/网关写入的日志)

从源代码运行

预先要求

为了能够从源代码运行解决方案, 应在你的计算机上安装并运行以下工具:

  • SQL Server 2015+ (可以是 express edition)

  • Redis 5.0+

  • RabbitMQ 3.7.11+

  • MongoDB 4.0+

  • ElasticSearch 6.6+

  • Kibana 6.6+ (可选,建议显示日志)

打开并构建Visual Studio解决方案

  • 在Visual Studio 2017 (15.9.0+)中打开samples\MicroserviceDemo\MicroserviceDemo.sln.

  • samples\MicroserviceDemo文件夹中的命令行运行dotnet restore命令.

  • 在Visual Studio中构建解决方案.

还原数据库

samples\MicroserviceDemo\databases文件夹中打开MsDemo_Identity.zipMsDemo_ProductManagement.zip并恢复到SQL Server.

请注意:这些数据库在解决方案中具有EF Core迁移,但它们没有种子数据,尤其是IdentityServer4所需的配置. 因此,恢复数据库要容易得多.

运行项目

按以下顺序运行项目(右键单击每个项目设置为启动项目,按Ctrl+F5运行,无需调试):

  • AuthServer.Host

  • IdentityService.Host

  • BloggingService.Host

  • ProductService.Host

  • InternalGateway.Host

  • BackendAdminAppGateway.Host

  • PublicWebSiteGateway.Host

  • BackendAdminApp.Host

  • PublicWebSite.Host

解决方案简介

Visual Studio解决方案由多个项目组成,每个项目在系统中具有不同的角色:

640?wx_fmt=png

应用程序(Applications)

这些是具有用户界面以与用户交互并使用系统的实际应用程序.

  • AuthServer.Host: 托管IdentityServer4以向其他服务和应用程序提供身份验证服务. 它是一个单点登录服务器,包含登录页面.

  • BackendAdminApp.Host: 这是一个后端管理应用程序,用于托管身份和产品管理模块的UI.

  • PubicWebSite.Host: 作为包含简单产品列表页面和博客模块UI的公共网站.

  • ConsoleClientDemo: 一个简单的控制台应用程序,用于演示C#应用程序中使用服务.

网关/BFF(前端后端)

网关用于为应用程序提供单一入口点.它还可以用于速率限制,负载平衡等. 使用Ocelot类库.

  • BackendAdminAppGateway.Host: 由BackendAdminApp.Host应用程序用作后端.

  • PublicWebSiteGateway.Host: 由PublicWebSite.Host应用程序用作后端.

  • InternalGateway.Host: 用于服务间通信(微服务之间的通信).

微服务

微服务没有UI,但暴露了一些REST API.

  • IdentityService.Host: 托管用于管理用户和角色的ABP Identity模块. 它没有其他服务,仅托管Identity模块的API.

  • BloggingService.Host: 托管ABP博客模块,该模块用于管理博客和帖子(典型的博客应用程序). 它没有其他服务,仅托管Blogging模块的API.

  • ProductService.Host: 托管用于管理产品的产品模块(位于解决方案内). 它还包含用于创建/更新产品管理数据库架构的EF Core迁移.

模块

  • 产品: 使用模块开发最佳实践开发的分层模块. 它可以嵌入到单个应用程序中,也可以通过单独部署API和UI作为微服务托管(如本演示解决方案中所述).

数据库

此解决方案使用多个数据库:

  • MsDemo_Identity: 一个SQL数据库. 默认使用** SQL Server **,但可以是EF Core支持的任何DBMS. 由AuthServer和IdentityService共享. 审计日志,权限和设置也存储在此数据库中(虽然它们可以轻松拥有自己的数据库,共享相同的数据库以保持简单).

  • MsDemo_ProductManagement: 一个SQL数据库. 同样默认使用 SQL Server,但可以是EF Core支持的任何DBMS. 由ProductService用作专用数据库.

  • MsDemo_BloggingMongoDB数据库. 由BloggingService使用.

  • Elasticsearch: 用于在Serilog上写日志.

应用

认证服务器 (AuthServer.Host)

所有其他服务和应用程序都使用此项目进行身份验证和单点登录. 主要使用IdentityServer4来提供这些服务. 它使用了一些预构建ABP模块 如 IdentityAudit Logging 和 Permission Management.

数据库和EF Core配置

此应用程序使用SQL数据库(将其命名为MsDemo_Identity)并通过Entity Framework Core迁移维护其架构.

它有一个名为AuthServerDbContext的DbContext,定义如下:

OnModelCreating方法中, 你会看到 ConfigureX() 方法调用. 具有数据库模式的模块通常声明这样的扩展方法,以便为其自己的实体配置EF Core映射. 这是一种灵活的方法, 可以在其中安排数据库和模块; 可以为每个模块使用不同的数据库,或者将它们中的一些组合在一个共享数据库中. 在AuthServer项目中,我们决定在单个物理数据库中将单个EF Core DbContext中的多个模块模式组合在一起. 这些模块是Identity,IdentityServer,AuditLogging,PermissionManagement和SettingManagement模块.

请注意,此DbContext仅用于数据库迁移. 所有模块都有自己的DbContext类,模块在运行时使用这些类.

用户界面

AuthServer有一个简单的主页,如果当前用户已登录,则显示当前用户信息:

640?wx_fmt=png

它还提供登录和注册页面:

640?wx_fmt=png

这些页面不包含在项目本身中. 相反,AuthServer项目使用带有IdentityServer扩展的预构建ABP帐户模块. 这意味着它还可以充当具有必要UI和逻辑的OpenId Connect服务器.

依赖

  • RabbitMQ 用于向其他服务发送消息.

  • Redis 用于分布式/共享缓存.

  • Elasticsearch 用于存储日志.

后端管理应用程序 (BackendAdminApp.Host)

这是一个Web应用程序,用于管理系统中的用户,角色,权限和产品.

认证

BackendAdminApp重定向到AuthServer进行身份验证. 用户输入正确的用户名和密码后,页面将再次重定向到后端应用程序. 身份验证配置在BackendAdminAppHostModule类中设置:

  • 它将"Cookies"身份验证添加为主要身份验证类型.

  • "oidc"身份验证配置为使用AuthServer应用程序作为身份验证服务器.

  • 它需要额外的身份范围(scopes) roleemail and phone.

  • It requires the API resource scopes BackendAdminAppGatewayIdentityService and ProductServicebecause it will use these services as APIs.

  • 它需要API资源范围 BackendAdminAppGatewayIdentityService 和 ProductService,因为它将这些服务用作API.

IdentityServer客户端设置存储在appsettings.json文件中:

用户界面

BackendAdminApp.Host项目本身没有单个UI元素/页面. 它仅用于提供身份和产品管理模块的UI页面.

BackendAdminAppHostModule将依赖关系添加到AbpIdentityWebModule(Volo.Abp.Identity.Web 包)和ProductManagementWebModule(ProductManagement.Web项目)为此目的.

用户管理页面的屏幕截图:

640?wx_fmt=png

来自权限的权限管理模式的屏幕截图:

640?wx_fmt=png

使用微服务

后端管理应用程序通过后端管理网关对所有操作使用Identity和Product微服务(BackendAdminAppGateway.Host).

远程端点

appsettings.json文件包含RemoteServices部分,用于声明远程服务端点. 每个微服务通常都有不同的端点. 但是,此解决方案使用API网关模式为应用程序提供单个端点:

http://localhost:65115/ 是 BackendAdminAppGateway.Host 项目的URL. 它知道身份和产品服务的位置.

HTTP Clients

ABP应用程序模块通常提供C#客户端库以轻松地使用服务(API)(它们通常使用ABP框架的Dynamic C# API客户端). 这意味着如果你需要使用Identity Service API, 你可以引用其客户端软件包,并通过提供的接口轻松使用API.

为此BackendAdminAppHostModule类声明了AbpIdentityHttpApiClientModuleProductManagementHttpApiClientModule的依赖关系.

一旦引用这些客户端软件包,就可以直接注入应用程序服务接口(例如IIdentityUserAppService)并使用其方法,如本地方法调用. 它实际上通过HTTP调用到相关服务端点的远程服务调用.

传递访问令牌(Access Token)

由于微服务需要身份验证和授权,因此每个远程服务调用都应包含Authentication头. 该头是从当前用户的当前HttpContext中的access_token获得的. 当你使用Volo.Abp.Http.Client.IdentityModel包时,会自动执行此操作. BackendAdminAppHostModule声明对此包和相关的AbpHttpClientIdentityModelModule类的依赖. 它集成到上面解释的HTTP客户端.

依赖

  • Redis 用于分布式/共享缓存.

  • Elasticsearch 用于存储日志.

公共网站 (PublicWebSite.Host)

这是一个公共网站项目,具有Web博客和产品列表页面.

认证

公共网站可以在不登录的情况下显示博客文章和产品列表. 如果你登录,你还可以管理博客. 它重定向到AuthServer进行身份验证. 用户输入正确的用户名和密码后,页面将再次重定向到公共网站应用程序. 身份验证配置在PublicWebSiteHostModule类中设置:

  • 它将"Cookies"身份验证添加为主要身份验证类型.

  • "oidc"身份验证配置为使用AuthServer应用程序作为身份验证服务器.

  • 它需要额外的身份范围 roleemail and phone.

  • 它需要API资源范围 PublicWebSiteGateway,BloggingServiceProductService,因为它将这些服务用作API.

IdentityServer客户端设置存储在appsettings.json文件中:

用户界面

PublicWebSite.Host项目有一个列出产品的页面 (Pages/Products.cshtml). 它还使用博客模块中的UI. 为此PublicWebSiteHostModule加入了BloggingWebModule(Volo.Blogging.Web 包)的依赖项.

产品页面的屏幕截图:

640?wx_fmt=png

使用微服务

公共网站应用程序使用Blogging和Product微服务通过公共网站网关进行所有操作(PublicWebSiteGateway.Host).

远程端点

appsettings.json文件包含RemoteServices部分,用于声明远程服务端点. 每个微服务通常都有不同的端点. 但是,此解决方案使用API网关模式为应用程序提供单个端点:

http://localhost:64897/ 是PublicWebSiteGateway.Host项目的URL. 它知道Blogging和产品服务的位置.

HTTP Clients

PublicWebSiteHostModule类声明BloggingHttpApiClientModuleProductManagementHttpApiClientModule的依赖关系,以便能够为这些服务使用远程HTTP API.

传递访问令牌(Access Token)

正如后端管理应用程序部分中所述, Public Web Site项目还使用AbpHttpClientIdentityModelModuleaccess_token传递给调用服务进行身份验证.

Dependencies

  • Redis 用于分布式/共享缓存.

  • Elasticsearch 用于存储日志.

控制台客户端演示

最后,该解决方案包括一个非常简单的控制台应用程序,名为ConsoleClientDemo,它通过AuthServer进行身份验证来使用Identity和Product服务. 它使用内部网关(InternalGateway.Host)来执行HTTP API调用.

远程服务配置

appsettings.json文件中的RemoteService配置很简单:

http://localhost:65129/ 是内部网关的URL. 对服务的所有API调用都是通过此URL执行的.

身份验证(IdentityServer客户端)配置

appsettings.json还有一个IdentityServer身份验证配置:

此示例使用client_credentials 授予类型,该类型需要ClientIdClientSecret进行身份验证过程. 还有其他授予类型. 例如, 你可以使用以下配置切换到password(Resource Owner Password)授予类型:

除客户端凭据外,Resource Owner Password还需要UserName 和 UserPassword. 此授权类型对于代表用户调用远程服务很有用.

Scope 声明API(和网关)以授予访问权限. 此应用程序使用内部网关.

HTTP Client依赖

ConsoleClientDemoModuleAbpIdentityHttpApiClientModuleProductManagementHttpApiClientModule有依赖关系,以便使用Identity和Product API. 它还具有“AbpHttpClientIdentityModelModule”依赖性,可通过IdentityServer进行身份验证.

使用服务

使用这些服务非常简单. 请参阅ClientDemoService类,它只是注入IIdentityUserAppServiceIProductAppService并使用它们. 该类还显示了使用HttpClient对象的手动HTTP调用. 有关详细信息,请参阅ClientDemoService的源代码.

API网关/ BFF(前端后端)

网关用于为应用程序提供 单一入口点. 通过这种方式,应用程序仅处理单个服务地址(API端点),而不是每个服务的不同地址. 网关还用于速率限制,安全性,身份验证,负载平衡和更多要求.

"后端前端"(BFF)是一种常见的架构模式,可为每种不同的应用程序/客户端类型构建专注而专业的网关. 此解决方案使用此模式并具有多个网关.

此解决方案使用Ocelot库来构建API网关. 它是ASP.NET核心广泛接受的API网关库.

后端管理应用程序网关 (BackendAdminAppGateway.Host)

这是"后端管理应用程序"的后端(服务器端API)(不要混淆命名;后端管理应用程序实际上是前端Web应用程序,但系统管理员而不是普通用户使用).

认证

此网关使用IdentityServer Bearer身份验证并配置如下:

AddIdentityServerAuthentication 扩展方法来自 IdentityServer4.AccessTokenValidation 包, IdentityServer4项目的一部分 (参见 identityserver文档).

ApiName是受保护的API,在这种情况下是BackendAdminAppGateway. 因此,此解决方案将网关定义为API资源. 其余配置与声明映射(计划在下一个ABP版本中自动化)相关. appsettings.json中的身份验证相关的配置很简单:

Ocelot配置

Ocelot需要知道微服务的真实URL才能重定向HTTP请求. 此网关的配置如下所示:

ReRoutes是一个URL映射数组. GlobalConfiguration部分中的BaseUrl是该网关的URL(Ocelot需要知道自己的URL). 参见 ocelot文档 更好地了解配置.

Ocelot is a finalizer ASP.NET Core middleware and should be written as the last item in the pipeline:

Ocelot是一个终结ASP.NET核心中间件,应该写成管道中的最后一项:

它根据上面的配置处理和重定向请求.

ABP 配置端点

ABP提供了一些内置API,以从服务器获取一些配置和信息. 例子:

  • /api/abp/application-configuration 返回本地化文本,权限和设置值 (http://localhost:65115/api/abp/application-configuration).

  • /Abp/ServiceProxyScript 返回动态javascript代理以从javascript客户端调用服务 (http://localhost:65115/Abp/ServiceProxyScript ).

这些端点应由网关服务提供,而不是由微服务提供. 微服务只能知道与该微服务相关的权限. 但是,一旦正确配置,网关就可以将多个服务的权限值聚合为一个更适合客户端的列表.

为此, ASP.NET Core管道配置为通过MVC而不是Ocelot处理某些特定路由. 为了实现这一点,MapWhen扩展方法使用如下:

当请求路径以 /api/abp/ 或 /Abp/.开头时,此配置使用标准MVC中间件.

Swagger

此网关配置为使用swagger UI, 这是一种用于发现和测试HTTP API的流行工具. 通常,Ocelot不支持在swagger上显示API,因为它无法了解每个微服务API的详细信息. 但是当你遵循ABP分层模块架构最佳实践时,它是可能的.

BackendAdminAppGatewayHostModule将依赖性添加到AbpIdentityHttpApiModule(Volo.Abp.Identity.HttpApi包)和ProductManagementHttpApiModule(ProductManagement.HttpApi 项目)以包含它们的HTTP API控制器. 通过这种方式,swagger可以发现它们. 虽然它引用了API层,但它没有引用应用程序服务的实现,因为它们将在相关的微服务端点中运行,并由Ocelot根据请求URL重定向.

无论如何,当你打开URLhttp://localhost:65115/swagger/index.html时, 你将看到所有已配置的微服务的API.

权限管理

后端管理应用程序提供权限管理UI(之前见过),并使用此网关获取/设置权限. 权限管理API托管在网关内,而不是单独的服务. 这是一个设计决策,但如果您愿意,它可以作为另一个微服务托管.

Dependencies

  • RabbitMQ 用于向其他服务发送消息.

  • Redis 用于分布式/共享缓存.

  • Elasticsearch 用于存储日志.

公共网站网关 (PublicWebSiteGateway.Host)

这是"公共网站"应用程序的后端(服务器端API网关).

认证

此网关使用IdentityServer Bearer身份验证并配置如下:

AddIdentityServerAuthentication 扩展方法来自 IdentityServer4.AccessTokenValidation包, IdentityServer4项目的一部分 (参见 identityserver文档).

ApiName是受保护的API,在这种情况下是PublicWebSiteGateway. 其余配置与声明映射(计划在下一个ABP版本中自动化)相关. 与appsettings.json中的身份验证相关的配置很简单:

Ocelot配置

Ocelot需要知道微服务的真实URL才能重定向HTTP请求. 此网关的配置如下所示:

参见 ocelot文档 更好地了解配置.

其它

请参阅"后端管理应用程序网关"部分中的"ABP配置端点"和"Swagger"主题,这些主题与此网关非常相似.

依赖

  • RabbitMQ 用于向其他服务发送消息.

  • Redis 用于分布式/共享缓存.

  • Elasticsearch 用于存储日志.

内部网关 (InternalGateway.Host)

该网关不是BFF. 它专为微服务间通信而设计,不会公开.

认证

此网关使用IdentityServer Bearer身份验证并配置如下:

AddIdentityServerAuthentication 扩展方法来自 IdentityServer4.AccessTokenValidation包, IdentityServer4项目的一部分 (参见 identityserver文档).

ApiName是受保护的API,在这种情况下是InternalGateway. 其余配置与声明映射(计划在下一个ABP版本中自动化)相关. 与appsettings.json中的身份验证相关的配置很简单:

Ocelot 配置

Ocelot需要知道微服务的真实URL才能重定向HTTP请求. 此网关的配置如下所示:

ReRoutes配置涵盖了系统中的所有微服务. 参见 ocelot文档 更好地了解配置.

其它

请参阅"后端管理应用程序网关"部分中的"ABP配置端点"和"Swagger"主题,这些主题与此网关非常相似.

依赖

  • RabbitMQ 用于向其他服务发送消息.

  • Redis 用于分布式/共享缓存.

  • Elasticsearch 用于存储日志.

微服务

微服务是独立的HTTP API,它们以分布式方式实现系统业务.

  • 它们由应用程序和其他微服务通过网关和HTTP API使用.

  • 他们可以提升或注册系统中的事件.

  • 它们可以通过异步消息传递相互通信.

Identity Service (IdentityService.Host)

此服务提供用户和角色管理API.

数据库

与AuthServer应用程序共享相同的数据库(MsDemo_Identity).

Identity模块

该服务实际上只托管ABP身份包/模块. 不包含任何API本身. 为了托管它,添加以下依赖项:

  • AbpIdentityHttpApiModule (Volo.Abp.Identity.HttpApi 包) 提供身份API.

  • AbpIdentityApplicationModule (Volo.Abp.Identity.Application 包)承载模块的应用程序和域层的实现.

  • AbpIdentityEntityFrameworkCoreModule (Volo.Abp.Identity.EntityFrameworkCore 包) 使用EF Core作为数据库API.

请参阅module architecture best practice guide以更好地理解分层.

认证

这个微服务使用IdentityServerBearer身份验证,并配置如下:

ApiName是受保护的API,在这种情况下是IdentityService. 其余配置与声明映射(计划在下一个ABP版本中自动化)相关. 与appsettings.json中的身份验证相关的配置很简单:

Swagger

Swagger UI已配置,是此服务的默认页面. 如果你导航到URLhttp://localhost:63568/, 你将被重定向到swagger页面以查看和测试API.

依赖

  • RabbitMQ 用于向其他服务发送消息.

  • Redis 用于分布式/共享缓存.

  • Elasticsearch 用于存储日志.

博客服务 (BloggingService.Host)

此服务托管博客API.

数据库

它有一个专门的MongoDB数据库(MsDemo_Blogging)来存储博客和帖子. 它还使用MsDemo_Identity SQL数据库来审核日志,权限和设置. 因此,appsettings.json文件中有两个连接字符串:

博客模块

该服务实际上只托管ABP Blogging包/模块. 不包含任何API本身. 为了托管它,添加以下依赖项:

  • BloggingHttpApiModule (Volo.Blogging.HttpApi 包) 提供Blogging API.

  • BloggingApplicationModule (Volo.Blogging.Application 包) 承载模块的应用程序和域层的实现.

  • BloggingMongoDbModule (Volo.Blogging.MongoDB 包) 使用MongoDB作为数据库.

请参阅module architecture best practice guide以更好地理解分层.

认证

这个微服务使用IdentityServer Bearer身份验证,并配置如下:

ApiName是受保护的API,在这种情况下是BloggingService. 其余配置与声明映射(计划在下一个ABP版本中自动化)相关.与appsettings.json中的身份验证相关的配置很简单:

IdentityServer Client

此微服务还通过内部网关使用Identity微服务API, 因为在某些情况下它需要查询用户详细信息(username, email, phone, name and surname). 因此,它也是IdentityServer的客户端,并在appsettings.json文件中定义了一个部分:

由于它使用内部网关, 因此它还应配置网关的远程端点:

当你将UseCurrentAccessToken设置为false时,ABP会忽略当前HttpContext中的当前access_token,并使用上面定义的凭据对AuthServer进行身份验证.

为什么不在当前请求中使用当前用户的令牌? 因为,用户可能没有Identity模块所需的权限,因此它不能直接将当前身份验证令牌传递给Identity服务. 此外,某些博客服务API是匿名的(不需要经过身份验证的用户),因此在某些情况下,HTTP请求中没有"当前用户". 出于这些原因,应将Blogging服务定义为具有自己的凭据和权限的Identity服务的客户端.

如果检查MsDemo_Identity数据库中的AbpPermissionGrants表,则可以看到blogging-service-client的相关权限.

640?wx_fmt=png

Swagger

Swagger UI已配置,是此服务的默认页面. 如果你导航到URLhttp://localhost:62157/, 你将被重定向到swagger页面以查看和测试API.

依赖

  • RabbitMQ 用于向其他服务发送消息.

  • Redis 用于分布式/共享缓存.

  • Elasticsearch 用于存储日志.

产品服务 (ProductService.Host)

此服务托管产品管理API.

数据库和EF核心迁移

它有一个单独的SQL数据库,名为MsDemo_ProductManagement,用于产品管理模块. 它使用EF Core作为数据库提供程序,并具有名为ProductServiceMigrationDbContext的DbContext:

实际模型配置在modelBuilder.ConfigureProductManagement()扩展方法内完成. 此项目使用EF Core迁移维护数据库模式.

请注意,此DbContext仅用于数据库迁移. Product Management模块有自己的DbContext类,它在运行时使用(参见ProductManagement.EntityFrameworkCore项目中的ProductManagementDbContext类).

appsettings.json文件中有两个连接字符串:

Default连接字符串指向MsDemo_Identity数据库,该数据库用于审计日志记录,权限和设置存储. 产品模块使用ProductManagement连接字符串.

产品模块

该服务实际上只托管产品管理模块. 不包含任何API本身. 为了托管它,添加以下依赖项:

  • ProductManagementHttpApiModule 提供产品管理API.

  • ProductManagementApplicationModule 承载模块的应用程序和域层的实现.

  • ProductManagementEntityFrameworkCoreModule 使用EF Core作为数据库API.

请参阅module architecture best practice guide以更好地理解分层. 有关此模块的更多信息,请参阅下面的"产品管理"模块部分

认证

这个微服务使用IdentityServer Bearer身份验证,并配置如下:

ApiName是受保护的API,在这种情况下是ProductService. 其余配置与声明映射(计划在下一个ABP版本中自动化)相关. 与appsettings.json中的身份验证相关的配置很简单:

Swagger

Swagger UI已配置,是此服务的默认页面. 如果你导航到URLhttp://localhost:60244/, 你将被重定向到swagger页面以查看和测试API.

依赖

  • RabbitMQ 用于向其他服务发送消息.

  • Redis 用于分布式/共享缓存.

  • Elasticsearch 用于存储日志.

模块

ABP提供强大的基础架构,通过提供服务和架构,使模块化应用程序开发更容易(参见模块开发最佳实践指南).

此解决方案演示了如何在分布式体系结构中使用预构建的应用程序模块. 该解决方案还包括一个简单的"产品管理"模块,用于显示分层模块示例的实现.

产品管理

产品管理是一个由多个层和包/项目组成的模块:

640?wx_fmt=png

  • 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元素(页面,脚本,样式..等).

通过此分层的帮助,可以在单个应用程序中使用相同的模块作为包引用,或者用作在另一个服务器中运行的服务. 可以分离UI(Web)和API层,因此它们可以在不同的服务器中运行.

在此解决方案中,Web层在后端管理应用程序中运行,而API层由产品微服务托管.

本教程将重点介绍该模块的一些重要方面. 但是,建议查看源代码以便更好地理解.

Domain Layer

Product是这个模块的主要聚合根:

它的所有属性都有私有的set方法,可以防止属性从类中直接更改. 产品类通过自己的构造函数和方法确保其自身的完整性和有效性.

它有两个构造函数:

默认(无参数)构造函数是私有的,不在应用程序代码中使用. 这是必需的,因为大多数ORM在从数据库获取时需要在反序列化实体时使用无参数构造函数.

第二个构造函数是internal,这意味着它只能在域层内使用. 这强制在创建新的Product时使用ProductManager. 因为``ProductManager`应该在新产品创建上实现业务规则. 此构造函数仅需要最少的必需参数来创建具有一些可选参数的新产品. 它会检查一些简单的业务规则,以确保将实体创建为有效产品.

该类的其余部分具有操纵实体属性的方法. 例:

SetPrice方法用于以安全的方式更改产品的价格(通过检查验证规则).

SetStockCount是另一种用于更改产品库存数量的方法:

此方法还触发分布式事件,其中带有ProductStockCountChangedEto参数(Eto是传统的后缀代表Event Transfer Object,但不是必需的)通知产品库存数量的听众已发生变化. 任何订户都可以接收此事件并根据该知识执行操作.

RabbitMQ为此解决方案分发事件. 但是ABP通过提供必要的抽象来独立于消息代理Event Bus文档).

如前所述,该模块强制始终使用ProductManager来创建新的ProductProductManager是一个简单的域服务,定义如下:

  • 它检查之前是否使用过给定的代码. 否则抛出ProductCodeAlreadyExistsException.

  • 使用GuidGenerator(IGuidGenerator)服务来创建一个新的Guid.

  • 它将实体插入存储库.

因此,通过这种设计,产品代码的唯一性得到保证.

ProductCodeAlreadyExistsException是一个域/业务异常,定义如下:

PM:000001是发送给客户端的异常类型的代码,因此他们可以理解错误类型. 在这种情况下没有实现,但也可以本地化业务异常. 请参阅异常处理文档.

应用层

该模块的应用层有两个服务:

  • ProductAppService主要由后端管理应用程序用于管理(创建,更新,删除.)产品. 它需要许可才能执行任何操作.

  • 公共网站使用PublicProductAppService来向访问者显示产品列表. 它不需要任何权限,因为大多数访问者都没有登录到应用程序.

请注意; 每个应用程序分离应用程序层可能是更好的原则, 而不是将两个应用程序服务放入同一个项目中. 但是我们在这个解决方案中简化了它们.

例如,ProductAppService具有以下更新产品的方法:

  • 它定义了所需的权限(ProductManagementPermissions.Products.Update是一个值为ProductManagement.Update的常量)来执行此操作.

  • 获取产品的ID,DTO包含要更新的值.

  • 从存储库中获取相关的产品实体.

  • 使用Product类的相关方法(如SetName)来更改属性,因为它们是私有set方法,更改值的唯一方法是使用实体方法.

  • 通过使用ObjectMapper向客户端返回更新的ProductDto(客户端可能由于某种原因需要它).

实施可能会根据要求而有所不同. 此实现遵循此处提供的最佳实践.

其他层

请参阅源代码中的其他层.

基础设施

消息和RabbitMQ

异步消息传递是分布式系统中的关键概念. 它可以以松散耦合的方式与容错进行通信. 在发送消息时,它不要求双方都在线. 因此,它是微服务架构中广泛使用的通信模式.

分布式事件总线

分布式事件(事件总线)是一种消息传递方式,其中服务引发/触发事件,而其他服务注册/侦听这些事件,以便在发生重要事件时得到通知. ABP通过提供约定,服务和集成使分布式事件更易于使用.

您已经看到Product类使用以下代码行发布事件:

ProductStockCountChangedEto was defined as shown below:

该对象存储有关该事件的必要信息. 通过使用通用的ProductStockCountChangedEto参数实现IDistributedEventHandler接口,另一个服务可以轻松注册到此事件:

当你使用Volo.Abp.EventBus.RabbitMQ包时,所有集成和通信都由ABP框架完成.如果需要从实体发布事件,只需注入IDistributedEventBus并使用PublishAsync方法.

有关分布式事件系统的更多信息, 请参见Event Bus文档.

RabbitMQ配置

在此解决方案中, RabbitMQ用于消息传递和分布式事件.

Volo.Abp.EventBus.RabbitMQ包需要集成到RabbitMQ以用于分布式事件系统. 然后你需要为模块的AbpEventBusRabbitMqModule添加依赖项. 例如ProductServiceHostModule声明了这种依赖.

默认情况下,abpEventBusRabbitMqModuleappsettings.json获取配置. 例如产品服务具有以下配置:

缓存和Redis

分布式系统显然需要分布式和共享缓存,而不是每个服务的隔离内存缓存.

Redis在此解决方案中用作分布式缓存. 该解决方案使用Microsoft的标准Microsoft.Extensions.Caching.Redis包进行集成. 使用和配置此程序包时,所有应用程序和服务都使用Redis缓存. 有关详细信息请参阅Microsoft的文档.

该解决方案还使用Microsoft.AspNetCore.DataProtection.StackExchangeRedis包在Redis缓存上共享应用程序和服务之间的数据保护密钥.

Logging,Serilog,Elasticsearch和Kibana

该解决方案使用Serilog作为日志库. 它是一个广泛使用的库,有许多数据源集成,包括Elasticsearch.

使用类似于下面给出的代码块在Program.cs文件中完成日志配置:

这会配置多个日志输出目标:File和Elasticsearch.对于此示例,Application属性设置为ProductService. 这是一种区分单个数据库中多个服务的日志的方法. 然后, 你可以通过Application名称查询日志.

appsettings.json配置文件中读取Elasticsearch URL:

如果你使用Kibana, 它是一个与Elasticsearch完美集成的可视化工具,可以看到有关你的日志的一些UI:

640?wx_fmt=png

Figure - 一个仪表板,显示服务/应用程序的日志和错误计数.

640?wx_fmt=png

Figure - 日志条目列表

Kibana URL默认为http://localhost:5601/.

审计日志

ABP提供自动审计日志记录,详细保存每个请求(当前用户,浏览器/客户端,执行了哪些操作,哪些实体更改,甚至实体的哪些属性已更新). 有关详细信息,请参阅审计日志文档.

所有服务和应用程序都配置为编写审核日志. 审核日志将保存到MsDemo_Identity SQL数据库中. 因此,您可以从单个点查询所有应用程序的所有审核日志.

审核日志记录具有CorrelationId属性,可用于跟踪请求. 当服务在单个Web请求中调用另一个服务时,它们都会使用相同的CorrelationId保存审核日志. 请参阅数据库中的AbpAuditLogs表.

原文地址:https://cn.abp.io/documents/abp/latest/Samples/Microservice-Demo

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com
640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/316594.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CodeForces 1517G Starry Night Camping(网络流最小割)

CodeForces 1517G Starry Night Camping problem 洛谷链接 solution 这个平行四边形的脑洞我™真的长见识了 本题最离谱的要求就是:平行四边形的一条边平行于 xxx 轴。 而往往这种离谱要求就是正解的途径。(((φ(◎ロ◎;)φ))) 首先不观察也能知道&#xff0c…

Acwing 307. 连通图

Acwing 307. 连通图 题意: 求 N 个节点的无向连通图有多少个,节点有标号,编号为 1∼N。 例如下列图示,三个节点的无向连通图共 4 个。 题解: 用py写 代码: def c(n, m):n int(n)m int(m)ret 1for i in range(…

Acwing 309. 装饰围栏

Acwing 309. 装饰围栏 题意: 有n个模板,长度分别是1到N,现在按照高低交错的方式排列模板,能到的很多种排列的方案。 每个方案都可以写作一个长度为N的序列,序列中的个元素是木板的长度,把这些序列按照字典…

CodeForces 901D Weighting a Tree(结论)

problem 洛谷链接 注意:保证 C[v]C[v]C[v] 的奇偶性与顶点 vvv 的度的奇偶性相同。 solution 先考虑树的情况。这是个经典问题了,从叶子往上推,对于一个点还差的边权就有这个点和其父亲的边来补足。最后判断根是否满足条件即可。 那么怎…

CF938E Max History

CF938E Max History 题意&#xff1a; 我们定义f(a)为&#xff1a; 1、开始时&#xff0c;f(a)0,M1。 2、对于每个2<i<n,如果a[M]<a[i],那么f(a)f(a)a[M],Mi。 现在对于一个给定的数组a&#xff0c;求其所有排列的f(a)之和&#xff0c;答案对1e97取模。 题解&am…

CodeForces 1491G Switch and Flip(结论)

problem 洛谷链接 solution 弱化版&#xff1a;如果不考虑翻面&#xff0c;那就是转化为若干个环的问题了。 这里我们尝试用同样的思路解决。 首先明确几个硬币一次交换后的等价情况&#xff0c;如图&#xff08;灰色表示反面&#xff09; &#xff08;u→vu\rightarrow …

ASP.NET Core 借助 K8S 玩转容器编排

Production-Grade Container Orchestration - Automated container deployment, scaling, and management. 生产级别的容器编排系统——自动化的容器部署、扩展和管理。1. 引言由于最近在学习微服务&#xff0c;所以就基于之前docker的基础上把玩一下k8s&#xff08;Kubernetes…

CF1009E Intercity Travelling

CF1009E Intercity Travelling 题意&#xff1a; 有一段路程&#xff0c;路程可以看作是从0到n的一条直线 如果从起点出发或者从休息点出发&#xff0c;连续驾驶k千米&#xff0c;则需要消耗的体能为a1…ak 每个整点都有可能拥有一个休息点&#xff0c;每个休息点存在或者不存…

CodeForces 1396E Distance Matching(构造+树的重心+dfs+set)

problem 洛谷链接 solution 这种要求值和恰好为 kkk 的题目&#xff0c;一般要先明确值和的取值范围。 所以我们先来确定一下值和的最小值和最大值。 将一条路径拆成若干条边&#xff0c;单独计算每条边对路径的贡献。 假设一条边将树划分成 S,TS,TS,T 集合。因为 nnn 为…

程序员修神之路--

写在开始一般来说有两种策略用来在并发线程中进行通信&#xff1a;共享数据和消息传递。使用共享数据方式的并发编程面临的最大的一个问题就是数据条件竞争。处理各种锁的问题是让人十分头痛的一件事。传统多数流行的语言并发是基于多线程之间的共享内存&#xff0c;使用同步方…

Lawn of the Dead

Lawn of the Dead 题意&#xff1a; 有一个N * M的方格&#xff0c;我们从(1,1)出发&#xff0c;只能向右走或者向下走&#xff0c;存在一些障碍&#xff0c;问有多少格子是我们所能到达的 2<n,m,k<1e5 题解&#xff1a; 所有的点减去不能到达的点的个数&#xff0c;…

.Netcore 2.0 Ocelot Api网关教程(8)- 缓存

Ocelot中使用 CacheManager 来支持缓存&#xff0c;官方文档中强烈建议使用该包作为缓存工具。以下介绍通过使用CacheManager来实现Ocelot缓存。1、通过Nuget添加 Ocelot.Cache.CacheManager 包在OcelotGetway项目中添加引用&#xff1a;2、修改 Startup 中的 ConfigureService…

Acwing 273. 分级

Acwing 273. 分级 题意&#xff1a; 给定一个长度为N的序列A&#xff0c;现在构造一个长度为N的序列B&#xff0c;满足&#xff0c;B是非严格单增。最小化S∑i1N∣Ai−Bi∣\sum_{i1}^{N}|A_i-B_i|∑i1N​∣Ai​−Bi​∣ 题解&#xff1a; 引理&#xff1a; 一定存在一组最优…

.NET Core 给使用.NET的公司所带来的机遇

今晚在余晟的微信公众号看到了一篇文章《从.NET/C#开发的“后继无人”说起》。 这篇文章以从.NET/C#开发的“后继无人” 引出了推广极客时间的课程 刘超的《趣谈Linux操作系统》&#xff0c;通篇看下来这明显是刘超的《趣谈Linux操作系统》的软文。软文地址&#xff1a;https:/…

Git Flow分支策略与Azure DevOps相关功能简介

想了很久&#xff0c;还是写这么一篇文章来总结一下有关分支策略和DevOps的一些内容吧。其实&#xff0c;DevOps相关的内容并不是我的工作范围&#xff0c;不过对于敏捷开发、DevOps、项目管理等等这一系列的与开发过程相关的内容&#xff0c;我还是有些经验的&#xff0c;也就…

AcWing 274. 移动服务

题意&#xff1a; 2<L<200 1<N<1000 题解&#xff1a; 一共就三个员工&#xff0c;我们可以在状态中记录三个员工的位置&#xff1b; 有&#xff1a;dp[i][x][y][z]:第i个工作完成后&#xff0c;三个员工的坐标分别是x&#xff0c;y&#xff0c;z&#xff0c;的最…

NSwag 和 ASP.NET Core

NSwag 提供了下列功能&#xff1a;能够使用 Swagger UI 和 Swagger 生成器。灵活的代码生成功能。借助 NSwag&#xff0c;无需使用现有 API。也就是说&#xff0c;可使用包含 Swagger 的第三方 API&#xff0c;并生成客户端实现。 使用 NSwag&#xff0c;可以加快开发周期&…

Acwing 276. I-区域

Acwing 276. I-区域 题意&#xff1a; 在 NM 的矩阵中&#xff0c;每个格子有一个权值&#xff0c;要求寻找一个包含 K 个格子的凸连通块&#xff08;连通块中间没有空缺&#xff0c;并且轮廓是凸的&#xff09;&#xff0c;使这个连通块中的格子的权值和最大。 注意&#xf…

开源Dapper的Lambda扩展-Sikiro.Dapper.Extension V2.0

去年我在业余时间&#xff0c;自己整了一套dapper的lambda表达式的封装&#xff0c;原本是作为了一个个人的娱乐项目&#xff0c;当时也只支持了Sql Server数据库。随之开源后&#xff0c;有不少朋友也对此做了试用&#xff0c;也对我这个项目提出了不少的建议。因此我在最近公…

Acwing 277. 饼干

Acwing 277. 饼干 题意&#xff1a; 圣诞老人共有 M 个饼干&#xff0c;准备全部分给 N 个孩子。 每个孩子有一个贪婪度&#xff0c;第 i 个孩子的贪婪度为 g[i]。 如果有 a[i] 个孩子拿到的饼干数比第 i 个孩子多&#xff0c;那么第 i 个孩子会产生 g[i]a[i] 的怨气。 给…