ABP Dapr 集成
本文档假设您已经熟悉 Dapr,并且希望在基于ABP的应用程序中使用它。
Dapr(分布式应用运行时)提供了简化微服务连接的API。它是一个主要由Microsoft支持的开源项目。它也是CNCF(云原生计算基金会)项目,并得到社区的信任。
ABP和Dapr有一些交叉的功能,如服务到服务的通信、分布式消息总线和分布式锁定。然而,它们的目的完全不同。ABP的目标是通过提供一种有见解的架构,并提供必要的基础设施库、可重用模块和工具来正确实现该架构,从而提供端到端的开发体验。而Dapr的目的是提供一个运行时,将常见的微服务通信模式与您的应用逻辑解耦。
ABP和Dapr可以在同一个应用程序中完美地协同工作。在Dapr功能与ABP相交的地方,ABP提供了一些包来提供更好的集成。您可以根据 其官方文档 使用其他Dapr功能,而无需ABP集成包。
ABP Dapr 集成包
ABP为Dapr集成提供了以下NuGet包:
- Volo.Abp.Dapr:核心Dapr集成包。所有其他包都依赖于此包。
- Volo.Abp.Http.Client.Dapr:将 ABP 的 动态 和 静态 C# API客户端代理系统与 Dapr 的 服务调用 构建块集成的包。
- Volo.Abp.EventBus.Dapr:使用Dapr的 发布与订阅 构建块实现ABP的分布式事件总线。使用此包,您可以发送事件,但不能接收。
- Volo.Abp.AspNetCore.Mvc.Dapr.EventBus:提供从 Dapr 的 发布与订阅 构建块接收事件的端点。使用此包可以发送和接收事件。
- Volo.Abp.DistributedLocking.Dapr:将 Dapr 的 分布式锁 构建块用于 ABP 的 分布式锁定 服务。
在以下部分中,我们将了解如何使用这些包在基于ABP的解决方案中使用Dapr。
基础
安装
本节介绍如何将核心Dapr集成包 Volo.Abp.Dapr 添加到您的项目中。如果您正在使用其他Dapr集成包,可以跳过本节,因为该包将被间接添加。
使用ABP CLI将 Volo.Abp.Dapr NuGet包添加到您的项目中:
- 如果尚未安装,请先安装 ABP CLI。
- 在要添加
Volo.Abp.Dapr包的.csproj文件目录中打开命令行(终端)。 - 运行
abp add-package Volo.Abp.Dapr命令。
如果想手动操作,请将 Volo.Abp.Dapr NuGet包安装到您的项目,并将 [DependsOn(typeof(AbpDaprModule))] 添加到项目内的 ABP模块 类中。
AbpDaprOptions
AbpDaprOptions 是主要的 选项类,您可以用它来配置全局Dapr设置。所有设置都是可选的,大多数情况下您不需要配置它们。 如果需要,可以在 模块类 的 ConfigureServices 方法中配置它:
Configure<AbpDaprOptions>(options =>
{
// ...
});
AbpDaprOptions 类的可用属性:
HttpEndpoint(可选):创建DaprClient对象时使用的HTTP端点。如果不指定,则使用默认值。GrpcEndpoint(可选):创建DaprClient对象时使用的gRPC端点。如果不指定,则使用默认值。DaprApiToken(可选):从应用程序向Dapr发送请求时使用的 Dapr API令牌 。默认情况下从DAPR_API_TOKEN环境变量填充(该变量由Dapr在配置后设置)。详情请参阅本文档中的安全部分。AppApiToken(可选):用于验证来自Dapr的请求的 应用API令牌 。默认情况下从APP_API_TOKEN环境变量填充(该变量由Dapr在配置后设置)。详情请参阅本文档中的安全部分。
或者,您可以在 appsettings.json 文件的 Dapr 部分中配置这些选项。示例:
"Dapr": {
"HttpEndpoint": "http://localhost:3500/"
}
IAbpDaprClientFactory
IAbpDaprClientFactory 可用于创建 DaprClient 或 HttpClient 对象以在Dapr上执行操作。它使用 AbpDaprOptions,因此您可以在中心位置配置设置。
使用示例:
public class MyService : ITransientDependency
{
private readonly IAbpDaprClientFactory _daprClientFactory;
public MyService(IAbpDaprClientFactory daprClientFactory)
{
_daprClientFactory = daprClientFactory;
}
public async Task DoItAsync()
{
// 使用默认选项创建 DaprClient 对象
DaprClient daprClient = await _daprClientFactory.CreateAsync();
/* 通过配置 DaprClientBuilder 对象
* 创建 DaprClient 对象 */
DaprClient daprClient2 = await _daprClientFactory
.CreateAsync(builder =>
{
builder.UseDaprApiToken("...");
});
// 创建 HttpClient 对象
HttpClient httpClient = await _daprClientFactory.CreateHttpClientAsync("target-app-id");
}
}
CreateHttpClientAsync 方法还接受可选的 daprEndpoint 和 daprApiToken 参数。
您可以在应用程序中使用Dapr API来创建客户端对象。建议使用
IAbpDaprClientFactory,但并非必需。
C# API 客户端代理集成
ABP 可以 动态 或 静态 生成代理类,以便从Dotnet客户端应用程序调用您的HTTP API。在分布式系统中使用HTTP API是非常有意义的。Volo.Abp.Http.Client.Dapr 包配置了客户端代理系统,使其在应用程序之间的通信中使用Dapr的服务调用构建块。
安装
使用ABP CLI将 Volo.Abp.Http.Client.Dapr NuGet包添加到您的项目中(添加到客户端):
- 如果尚未安装,请先安装 ABP CLI。
- 在要添加
Volo.Abp.Http.Client.Dapr包的.csproj文件目录中打开命令行(终端)。 - 运行
abp add-package Volo.Abp.Http.Client.Dapr命令。
如果想手动操作,请将 Volo.Abp.Http.Client.Dapr NuGet包安装到您的项目,并将 [DependsOn(typeof(AbpHttpClientDaprModule))] 添加到项目内的 ABP模块 类中。
配置
安装 Volo.Abp.Http.Client.Dapr NuGet包后,您需要做的就是在 appsettings.json 中或使用 AbpRemoteServiceOptions 选项类 来配置ABP的远程服务选项。
示例:
{
"RemoteServices": {
"Default": {
"BaseUrl": "http://dapr-httpapi/"
}
}
}
此示例中的 dapr-httpapi 是Dapr配置中服务器应用程序的应用ID。
远程服务名称(此示例中为 Default)应与动态客户端代理的 AddHttpClientProxies 调用或静态客户端代理的 AddStaticHttpClientProxies 调用中指定的远程服务名称匹配。如果您的客户端与单个服务器通信,使用 Default 即可。但是,如果您的客户端使用多个服务器,通常会在 RemoteServices 配置中有多个键。一旦您将远程服务端点配置为Dapr应用ID,当您使用ABP的客户端代理系统时,它将自动工作并通过Dapr进行HTTP调用。
分布式事件总线集成
ABP的分布式事件总线 系统提供了一个便捷的抽象,允许应用程序通过事件进行异步通信。ABP与各种分布式消息系统(如RabbitMQ、Kafka和Azure)有集成包。Dapr 也有一个 发布与订阅构建块 ,用于相同的目的:分布式消息/事件。
ABP的 Volo.Abp.EventBus.Dapr 和 Volo.Abp.AspNetCore.Mvc.Dapr.EventBus 包使得可以将Dapr基础设施用于ABP的分布式事件总线。
Volo.Abp.EventBus.Dapr 包可被任何类型的应用程序(例如,控制台或ASP.NET Core应用程序)用于通过Dapr发布事件。要能够接收消息(通过订阅事件),您需要安装 Volo.Abp.AspNetCore.Mvc.Dapr.EventBus 包,并且您的应用程序必须是ASP.NET Core应用程序。
安装
如果您的应用程序是ASP.NET Core应用程序,并且您想要发送和接收事件,则需要按照以下说明安装 Volo.Abp.AspNetCore.Mvc.Dapr.EventBus 包:
- 如果尚未安装,请先安装 ABP CLI。
- 在要添加
Volo.Abp.AspNetCore.Mvc.Dapr.EventBus包的.csproj文件目录中打开命令行(终端)。 - 运行
abp add-package Volo.Abp.AspNetCore.Mvc.Dapr.EventBus命令。
如果想手动操作,请将 Volo.Abp.AspNetCore.Mvc.Dapr.EventBus NuGet包安装到您的项目,并将 [DependsOn(typeof(AbpAspNetCoreMvcDaprEventBusModule))] 添加到项目内的 ABP模块 类中。
如果您安装了 Volo.Abp.AspNetCore.Mvc.Dapr.EventBus 包,则无需安装 Volo.Abp.EventBus.Dapr 包,因为前者已经引用了后者。
如果您的应用程序不是ASP.NET Core应用程序,您将无法从Dapr接收事件,至少使用ABP的集成包不行(如果您想在不同类型的应用程序中接收事件,请参阅 Dapr文档)。但是,您仍然可以使用 Volo.Abp.EventBus.Dapr 包发布消息。在这种情况下,请按照以下步骤将此包安装到您的项目:
- 如果尚未安装,请先安装 ABP CLI。
- 在要添加
Volo.Abp.EventBus.Dapr包的.csproj文件目录中打开命令行(终端)。 - 运行
abp add-package Volo.Abp.EventBus.Dapr命令。
如果想手动操作,请将 Volo.Abp.EventBus.Dapr NuGet包安装到您的项目,并将 [DependsOn(typeof(AbpEventBusDaprModule))] 添加到项目内的 ABP模块 类中。
配置
您可以使用 AbpDaprEventBusOptions 选项类 来配置Dapr:
Configure<AbpDaprEventBusOptions>(options =>
{
options.PubSubName = "pubsub";
});
AbpDaprEventBusOptions 类的可用属性:
PubSubName(可选):通过DaprClient.PublishEventAsync方法发布消息时使用的pubsubName参数。默认值:pubsub。
ABP 订阅端点
ABP提供以下端点以从Dapr接收事件:
dapr/subscribe:Dapr使用此端点从应用程序获取订阅列表。ABP会自动为您的分布式事件处理程序类和带有Topic属性的自定义控制器操作返回所有订阅。api/abp/dapr/event:从Dapr接收所有事件的统一端点。ABP根据主题名称将事件分发给您的事件处理程序。
由于ABP将在内部调用
MapSubscribeHandler,因此您不应再手动调用它。 如果您想支持 CloudEvents 标准,可以在ASP.NET Core管道中使用app.UseCloudEvents()中间件。
使用
ABP 方式
您可以遵循 ABP的分布式事件总线文档 来了解如何以ABP的方式发布和订阅事件。您的应用程序代码无需更改即可使用Dapr发布-订阅。ABP将自动为您的实现了 IDistributedEventHandler 接口的事件处理程序类订阅Dapr。
ABP提供了 api/abp/dapr/event 端点作为统一的事件接收端点。
示例:使用 IDistributedEventBus 服务发布事件
public class MyService : ITransientDependency
{
private readonly IDistributedEventBus _distributedEventBus;
public MyService(IDistributedEventBus distributedEventBus)
{
_distributedEventBus = distributedEventBus;
}
public async Task DoItAsync()
{
await _distributedEventBus.PublishAsync(new StockCountChangedEto
{
ProductCode = "AT837234",
NewStockCount = 42
});
}
}
示例:通过实现 IDistributedEventHandler 接口订阅事件
public class MyHandler :
IDistributedEventHandler<StockCountChangedEto>,
ITransientDependency
{
public async Task HandleEventAsync(StockCountChangedEto eventData)
{
var productCode = eventData.ProductCode;
// ...
}
}
有关详细信息,请参阅 ABP的分布式事件总线文档。
使用 Dapr API
除了ABP的标准分布式事件总线系统外,您还可以使用Dapr的API来发布事件。
如果您直接使用Dapr API发布事件,则可能无法受益于ABP的标准分布式事件总线功能,例如发件箱/收件箱模式实现。
示例:使用 DaprClient 发布事件
public class MyService : ITransientDependency
{
private readonly DaprClient _daprClient;
public MyService(DaprClient daprClient)
{
_daprClient = daprClient;
}
public async Task DoItAsync()
{
await _daprClient.PublishEventAsync(
"pubsub", // pubsub 名称
"StockChanged", // 主题名称
new StockCountChangedEto // 事件数据
{
ProductCode = "AT837234",
NewStockCount = 42
}
);
}
}
示例:通过创建ASP.NET Core控制器订阅事件
public class MyController : AbpController
{
[HttpPost("/stock-changed")]
[Topic("pubsub", "StockChanged")]
public async Task<IActionResult> TestRouteAsync([FromBody] StockCountChangedEto model)
{
HttpContext.ValidateDaprAppApiToken();
// 使用事件进行处理
return Ok();
}
}
HttpContext.ValidateDaprAppApiToken() 扩展方法由ABP提供,用于检查请求是否来自Dapr。这是可选的。如果您想启用验证,则应配置Dapr以向您的应用程序发送应用API令牌。如果未配置,ValidateDaprAppApiToken() 不执行任何操作。更多信息,请参阅 Dapr的应用API令牌文档。另请参阅本文档中的 AbpDaprOptions 和 安全 部分。
有关使用Dapr API发送和接收事件的详细信息,请参阅 Dapr文档。
分布式锁
Dapr的分布式锁功能目前处于Alpha阶段,可能还不稳定。在这一点上,不建议用Dapr替换ABP的分布式锁。
ABP提供了一个 分布式锁定 抽象,以控制多个应用程序对共享资源的访问。Dapr也有一个 分布式锁构建块 。Volo.Abp.DistributedLocking.Dapr 包使ABP使用Dapr的分布式锁定系统。
安装
使用ABP CLI将 Volo.Abp.DistributedLocking.Dapr NuGet包添加到您的项目中(添加到客户端):
- 如果尚未安装,请先安装 ABP CLI。
- 在要添加
Volo.Abp.DistributedLocking.Dapr包的.csproj文件目录中打开命令行(终端)。 - 运行
abp add-package Volo.Abp.DistributedLocking.Dapr命令。
如果想手动操作,请将 Volo.Abp.DistributedLocking.Dapr NuGet包安装到您的项目,并将 [DependsOn(typeof(AbpDistributedLockingDaprModule))] 添加到项目内的 ABP模块 类中。
配置
您可以在 模块 的 ConfigureServices 方法中使用 AbpDistributedLockDaprOptions 选项类来配置Dapr分布式锁:
Configure<AbpDistributedLockDaprOptions>(options =>
{
options.StoreName = "mystore";
});
以下选项可用:
StoreName(必需):Dapr使用的存储名称。锁名称在同一存储中作用域。这意味着不同的应用程序可以在不同的存储中获取相同的锁名称。对于要控制访问的相同资源,请使用相同的存储名称。Owner(可选):DaprClient.Lock方法使用的owner值。如果不指定,ABP使用一个随机值,这在一般情况下是可行的。DefaultExpirationTimeout(可选):锁过期后的默认时间。默认值:2分钟。
使用
您可以注入并使用 IAbpDistributedLock 服务,正如 分布式锁定文档 中所解释的那样。
示例:
public class MyService : ITransientDependency
{
private readonly IAbpDistributedLock _distributedLock;
public MyService(IAbpDistributedLock distributedLock)
{
_distributedLock = distributedLock;
}
public async Task MyMethodAsync()
{
await using (var handle =
await _distributedLock.TryAcquireAsync("MyLockName"))
{
if (handle != null)
{
// 访问共享资源的代码
}
}
}
}
关于 TryAcquireAsync 方法,有两点需要注意,与ABP的标准用法不同:
- 当前未使用
timeout参数(即使您指定了它),因为Dapr不支持等待获取锁。 - Dapr使用过期超时系统(这意味着即使您不通过处置处理程序来释放锁,锁也会在该超时后自动释放)。但是,ABP的
TryAcquireAsync方法没有这样的参数。目前,您可以在应用程序中设置AbpDistributedLockDaprOptions.DefaultExpirationTimeout作为全局值。
如前所述,Dapr的分布式锁功能目前处于Alpha阶段,其API可能会更改。如果您愿意,可以按原样使用它,但要为未来的更改做好准备。目前,我们建议使用 DistributedLock 库,如ABP的 分布式锁定文档 中所述。
安全
如果您使用Dapr,您的应用程序中大部分或所有传入和传出的请求都会经过Dapr。Dapr使用两种API令牌来保护您的应用程序与Dapr之间的通信。
Dapr API 令牌
此令牌默认自动设置,通常您无需关心。
在 Dapr 中启用API令牌认证 文档描述了Dapr API令牌是什么以及如何配置。如果您想为应用程序启用它,请阅读该文档。
如果您启用了Dapr API令牌,则应在从应用程序向Dapr发送的每个请求中发送该令牌。AbpDaprOptions 定义了一个 DaprApiToken 属性,作为在应用程序中配置Dapr API令牌的中心点。
DaprApiToken 属性的默认值从 DAPR_API_TOKEN 环境变量设置,该环境变量在Dapr运行时由其设置。因此,大多数情况下,您无需在应用程序中配置 AbpDaprOptions.DaprApiToken。但是,如果需要配置(或覆盖)它,可以在模块类的 ConfigureServices 方法中执行,如以下代码块所示:
Configure<AbpDaprOptions>(options =>
{
options.DaprApiToken = "...";
});
或者,您可以在 appsettings.json 文件中设置它:
"Dapr": {
"DaprApiToken": "..."
}
设置后,当您使用 IAbpDaprClientFactory 时,将使用该令牌。如果您的应用程序中需要该值,可以注入 IDaprApiTokenProvider 并使用其 GetDaprApiToken() 方法。
应用 API 令牌
强烈建议启用应用API令牌验证。否则,例如,任何客户端都可以直接调用您的事件订阅端点,而您的应用程序会像发生了事件一样行动(如果事件订阅端点中没有其他安全策略)。
使用令牌认证验证来自 Dapr 的请求 文档描述了应用API令牌是什么以及如何配置。如果您想为应用程序启用它,请阅读该文档。
如果您启用了应用API令牌,则可以验证它以确保请求来自Dapr。ABP提供了有用的快捷方式来验证它。
示例:在事件处理HTTP API中验证应用API令牌
public class MyController : AbpController
{
[HttpPost("/stock-changed")]
[Topic("pubsub", "StockChanged")]
public async Task<IActionResult> TestRouteAsync([FromBody] StockCountChangedEto model)
{
// 验证应用API令牌!
HttpContext.ValidateDaprAppApiToken();
// 使用事件进行处理
return Ok();
}
}
HttpContext.ValidateDaprAppApiToken() 是ABP提供的扩展方法。如果HTTP头中缺少令牌或令牌错误(头名称为 dapr-api-token),它将抛出 AbpAuthorizationException。您也可以注入 IDaprAppApiTokenValidator 并在任何服务(不仅限于控制器类)中使用其方法来验证令牌。
如果您想设置(或覆盖)应用API令牌值,可以配置 AbpDaprOptions.AppApiToken。默认值由 APP_API_TOKEN 环境变量设置。您可以在模块类的 ConfigureServices 方法中更改它,如以下代码块所示:
Configure<AbpDaprOptions>(options =>
{
options.AppApiToken = "...";
});
或者,您可以在 appsettings.json 文件中设置它:
"Dapr": {
"AppApiToken": "..."
}
如果您的应用程序中需要该值,可以注入 IDaprApiTokenProvider 并使用其 GetAppApiToken() 方法。
抠丁客


