项目

ABP Dapr 集成

本文档假设您已经熟悉 Dapr,并且希望在基于ABP的应用程序中使用它。

Dapr(分布式应用运行时)提供了简化微服务连接的API。它是一个主要由Microsoft支持的开源项目。它也是CNCF(云原生计算基金会)项目,并得到社区的信任。

ABP和Dapr有一些交叉的功能,如服务到服务的通信、分布式消息总线和分布式锁定。然而,它们的目的完全不同。ABP的目标是通过提供一种有见解的架构,并提供必要的基础设施库、可重用模块和工具来正确实现该架构,从而提供端到端的开发体验。而Dapr的目的是提供一个运行时,将常见的微服务通信模式与您的应用逻辑解耦。

ABP和Dapr可以在同一个应用程序中完美地协同工作。在Dapr功能与ABP相交的地方,ABP提供了一些包来提供更好的集成。您可以根据 其官方文档 使用其他Dapr功能,而无需ABP集成包。

ABP Dapr 集成包

ABP为Dapr集成提供了以下NuGet包:

在以下部分中,我们将了解如何使用这些包在基于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 可用于创建 DaprClientHttpClient 对象以在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 方法还接受可选的 daprEndpointdaprApiToken 参数。

您可以在应用程序中使用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的分布式事件总线 系统提供了一个便捷的抽象,允许应用程序通过事件进行异步通信。ABP与各种分布式消息系统(如RabbitMQ、Kafka和Azure)有集成包。Dapr 也有一个 发布与订阅构建块 ,用于相同的目的:分布式消息/事件。

ABP的 Volo.Abp.EventBus.DaprVolo.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() 方法。

另请参阅

在本文档中