DaprClient 使用
生命周期管理
DaprClient 使用 TCP 套接字来访问网络资源,与 Dapr sidecar 进行通信。它实现了 IDisposable 接口,以便快速清理资源。
依赖注入
通过 AddDaprClient() 方法可以在 ASP.NET Core 中注册 Dapr 客户端。此方法接受一个可选的配置委托,用于配置 DaprClient,以及一个 ServiceLifetime 参数,允许您为注册的资源指定不同的生命周期,默认是 Singleton。
以下示例展示了如何使用默认值注册 DaprClient:
services.AddDaprClient();
您可以通过配置委托在 DaprClientBuilder 上指定选项来配置 DaprClient,例如:
services.AddDaprClient(daprBuilder => {
daprBuilder.UseJsonSerializerOptions(new JsonSerializerOptions {
WriteIndented = true,
MaxDepth = 8
});
daprBuilder.UseTimeout(TimeSpan.FromSeconds(30));
});
另一个重载允许访问 DaprClientBuilder 和 IServiceProvider,以便进行更高级的配置,例如从依赖注入容器中获取服务:
services.AddSingleton<SampleService>();
services.AddDaprClient((serviceProvider, daprBuilder) => {
var sampleService = serviceProvider.GetRequiredService<SampleService>();
var timeoutValue = sampleService.TimeoutOptions;
daprBuilder.UseTimeout(timeoutValue);
});
手动实例化
除了依赖注入,您还可以使用静态客户端构建器手动创建 DaprClient。
为了优化性能,建议创建一个长生命周期的 DaprClient 实例,并在整个应用程序中共享。DaprClient 是线程安全的,适合共享使用。
避免为每个操作创建一个新的 DaprClient 实例并在操作完成后释放它。
配置 DaprClient
在调用 .Build() 创建客户端之前,可以通过 DaprClientBuilder 类上的方法来配置 DaprClient。每个 DaprClient 对象的设置是独立的,创建后无法更改。
var daprClient = new DaprClientBuilder()
.UseJsonSerializerSettings( ... ) // 配置 JSON 序列化器
.Build();
默认情况下,DaprClientBuilder 会按以下顺序优先获取配置值:
- 直接提供给
DaprClientBuilder方法的值(例如UseTimeout(TimeSpan.FromSeconds(30))) - 从可选的
IConfiguration中提取的值,与环境变量名称匹配 - 从环境变量中提取的值
- 默认值
在 DaprClientBuilder 上配置
DaprClientBuilder 提供以下方法来设置配置选项:
UseHttpEndpoint(string): 设置 Dapr sidecar 的 HTTP 端点UseGrpcEndpoint(string): 设置 Dapr sidecar 的 gRPC 端点UseGrpcChannelOptions(GrpcChannelOptions): 设置 gRPC 通道选项UseHttpClientFactory(IHttpClientFactory): 配置DaprClient使用的HttpClient工厂UseJsonSerializationOptions(JsonSerializerOptions): 配置 JSON 序列化UseDaprApiToken(string): 为 Dapr sidecar 的身份验证提供令牌UseTimeout(TimeSpan): 指定与 Dapr sidecar 通信时的超时值
从 IConfiguration 配置
除了直接从环境变量获取配置值,您还可以通过 IConfiguration 提供这些值。
例如,在多租户环境中,您可能需要为环境变量添加前缀。以下示例展示了如何从环境变量中获取这些值到 IConfiguration,并移除前缀:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddEnvironmentVariables("test_"); // 获取所有以 "test_" 开头的环境变量,并移除前缀
builder.Services.AddDaprClient();
从环境变量配置
SDK 会读取以下环境变量来配置默认值:
DAPR_HTTP_ENDPOINT: Dapr sidecar 的 HTTP 端点,例如:https://dapr-api.mycompany.comDAPR_GRPC_ENDPOINT: Dapr sidecar 的 gRPC 端点,例如:https://dapr-grpc-api.mycompany.comDAPR_HTTP_PORT: 如果未设置DAPR_HTTP_ENDPOINT,则用于查找本地 HTTP 端点DAPR_GRPC_PORT: 如果未设置DAPR_GRPC_ENDPOINT,则用于查找本地 gRPC 端点DAPR_API_TOKEN: 设置 API 令牌
注意
如果同时指定了DAPR_HTTP_ENDPOINT 和 DAPR_HTTP_PORT,则会忽略 DAPR_HTTP_PORT 的端口值,而使用 DAPR_HTTP_ENDPOINT 上定义的端口。DAPR_GRPC_ENDPOINT 和 DAPR_GRPC_PORT 也是如此。配置 gRPC 通道选项
Dapr 使用 CancellationToken 进行取消,依赖于 gRPC 通道选项的配置,默认已启用。如果您需要自行配置这些选项,请确保启用 ThrowOperationCanceledOnCancellation 设置。
var daprClient = new DaprClientBuilder()
.UseGrpcChannelOptions(new GrpcChannelOptions { ... ThrowOperationCanceledOnCancellation = true })
.Build();
使用 DaprClient 进行取消
在 DaprClient 上执行异步操作的 API 接受一个可选的 CancellationToken 参数。这遵循 .NET 的标准惯例,用于可取消的操作。请注意,当取消发生时,不能保证远程端点停止处理请求,只能保证客户端已停止等待完成。
当操作被取消时,将抛出一个 OperationCancelledException。
理解 DaprClient 的 JSON 序列化
DaprClient 上的许多方法使用 System.Text.Json 序列化器执行 JSON 序列化。接受应用程序数据类型作为参数的方法将对其进行 JSON 序列化,除非文档明确说明了其他情况。
如果您有高级需求,建议阅读 System.Text.Json 文档。Dapr .NET SDK 不提供独特的序列化行为或自定义 - 它依赖于底层序列化器将数据转换为和从应用程序的 .NET 类型。
DaprClient 被配置为使用从 JsonSerializerDefaults.Web 配置的序列化器选项对象。这意味着 DaprClient 将使用 camelCase 作为属性名称,允许读取带引号的数字("10.99"),并将不区分大小写地绑定属性。这些是与 ASP.NET Core 和 System.Text.Json.Http API 一起使用的相同设置,旨在遵循可互操作的 Web 约定。
截至 .NET 5.0,System.Text.Json 对所有 F# 语言特性内置支持不佳。如果您使用 F#,您可能需要使用一个添加对 F# 特性支持的转换器包,例如 FSharp.SystemTextJson。
JSON 序列化的简单指导
如果您使用的功能集映射到 JSON 的类型系统,您在使用 JSON 序列化和 DaprClient 时的体验将会很顺利。这些是可以简化代码的通用指南。
- 避免继承和多态
- 不要尝试序列化具有循环引用的数据
- 不要在构造函数或属性访问器中放置复杂或昂贵的逻辑
- 使用与 JSON 类型(数值类型、字符串、
DateTime)清晰映射的 .NET 类型 - 为顶级消息、事件或状态值创建自己的类,以便将来可以添加属性
- 设计具有
get/set属性的类型,或者使用 支持的模式 用于 JSON 的不可变类型
多态性和序列化
DaprClient 使用的 System.Text.Json 序列化器在执行序列化时使用值的声明类型。
本节将使用 DaprClient.SaveStateAsync<TValue>(...) 作为示例,但建议适用于 SDK 暴露的任何 Dapr 构建块。
public class Widget
{
public string Color { get; set; }
}
...
// 将 Widget 值作为 JSON 存储在状态存储中
Widget widget = new Widget() { Color = "Green", };
await client.SaveStateAsync("mystatestore", "mykey", widget);
在上面的示例中,类型参数 TValue 的类型参数是从 widget 变量的类型推断出来的。这很重要,因为 System.Text.Json 序列化器将根据值的声明类型执行序列化。结果是 JSON 值 { "color": "Green" } 将被存储。
考虑当您尝试使用 Widget 的派生类型时会发生什么:
public class Widget
{
public string Color { get; set; }
}
public class SuperWidget : Widget
{
public bool HasSelfCleaningFeature { get; set; }
}
...
// 将 SuperWidget 值作为 JSON 存储在状态存储中
Widget widget = new SuperWidget() { Color = "Green", HasSelfCleaningFeature = true, };
await client.SaveStateAsync("mystatestore", "mykey", widget);
在此示例中,我们使用了一个 SuperWidget,但变量的声明类型是 Widget。由于 JSON 序列化器的行为由声明类型决定,它只看到一个简单的 Widget,并将保存值 { "color": "Green" },而不是 { "color": "Green", "hasSelfCleaningFeature": true }。
如果您希望 SuperWidget 的属性被序列化,那么最好的选择是用 object 覆盖类型参数。这将导致序列化器包含所有数据,因为它对类型一无所知。
Widget widget = new SuperWidget() { Color = "Green", HasSelfCleaningFeature = true, };
await client.SaveStateAsync<object>("mystatestore", "mykey", widget);
错误处理
当遇到故障时,DaprClient 上的方法将抛出 DaprException 或其子类。
try
{
var widget = new Widget() { Color = "Green", };
await client.SaveStateAsync("mystatestore", "mykey", widget);
}
catch (DaprException ex)
{
// 处理异常,记录日志,重试等
}
最常见的故障情况将与以下内容相关:
- Dapr 组件配置不正确
- 瞬时故障,例如网络问题
- 无效数据,例如 JSON 反序列化失败
在任何这些情况下,您都可以通过 .InnerException 属性检查更多异常详细信息。