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
实例并在操作完成后释放它。
在调用 .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.com
DAPR_GRPC_ENDPOINT
: Dapr sidecar 的 gRPC 端点,例如:https://dapr-grpc-api.mycompany.com
DAPR_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
也是如此。Dapr 使用 CancellationToken
进行取消,依赖于 gRPC 通道选项的配置,默认已启用。如果您需要自行配置这些选项,请确保启用 ThrowOperationCanceledOnCancellation 设置。
var daprClient = new DaprClientBuilder()
.UseGrpcChannelOptions(new GrpcChannelOptions { ... ThrowOperationCanceledOnCancellation = true })
.Build();
在 DaprClient 上执行异步操作的 API 接受一个可选的 CancellationToken
参数。这遵循 .NET 的标准惯例,用于可取消的操作。请注意,当取消发生时,不能保证远程端点停止处理请求,只能保证客户端已停止等待完成。
当操作被取消时,将抛出一个 OperationCancelledException
。
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 序列化和 DaprClient
时的体验将会很顺利。这些是可以简化代码的通用指南。
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)
{
// 处理异常,记录日志,重试等
}
最常见的故障情况将与以下内容相关:
在任何这些情况下,您都可以通过 .InnerException
属性检查更多异常详细信息。