Dapr全称Distributed Application Runtime,是一个以边车(Sidecar)方式运行的分布式应用运行时。对C#开发者来说,它把服务调用、状态存储、发布订阅、密钥管理这些常见分布式能力,从业务代码里剥离出来,交由独立的Dapr Sidecar处理。你的C#程序只需要通过HTTP或gRPC与本地Sidecar通信,就能获得跨语言、可插拔的基础设施支撑。

一、环境准备与项目结构
开始之前需要安装Dapr CLI与Docker,Dapr的默认组件如Redis、Zipkin都通过Docker容器运行。在Windows或Linux终端执行官方安装命令后,运行dapr init即可初始化本地环境。该命令会拉取Redis、Placement等镜像,并在本地启动Dapr控制面。
我们用两个C#项目演示:一个是订单服务(Web API),一个是库存服务(控制台消费者)。订单服务接收请求后,通过Dapr的PubSub组件发布消息;库存服务订阅该主题并扣减库存。两个项目都引用Dapr.Client NuGet包,版本需与Sidecar匹配。项目结构保持简单,重点在于理解调用路径。
// 订单服务 Program.cs 片段 var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); var app = builder.Build(); app.MapControllers(); app.Run();
二、使用Dapr Client进行状态管理
Dapr的状态管理API允许你把数据存入配置的State Store(如Redis),而不直接依赖具体数据库驱动。C#中通过Dapr.Client的DaprClient类操作。下面示例展示保存与读取订单状态,底层由Sidecar转发到Redis。
这种方式的优势是切换存储只需改Dapr组件配置文件,业务代码零改动。缺点是多一跳网络调用,延迟略高,因此不适合极高并发的本地缓存场景。以下代码演示用HTTP方式经由Sidecar端口3500写入状态:
using Dapr.Client;
var client = new DaprClientBuilder().Build();
// 保存订单状态,statestore为默认状态组件名
await client.SaveStateAsync("statestore", "order-1001", new { Product = "Book", Qty = 2 });
// 读取状态
var order = await client.GetStateAsync<dynamic>("statestore", "order-1001");
Console.WriteLine(order.Product);
三、发布订阅消息传递
PubSub是微服务解耦的关键。Dapr通过声明式组件接入Redis、Kafka等消息中间件。C#服务发布事件时,只需指定主题名,Sidecar负责投递;消费端用[Topic]特性绑定控制器方法即可。
下面订单服务在创建订单后发布事件。注意Dapr要求主题在组件配置中预先声明,否则会路由失败。消费端控制台应用使用Dapr.AspNetCore的订阅中间件,启动后Sidecar会自动注册订阅关系。
// 订单服务控制器发布消息
[HttpPost]
public async Task<IActionResult> Create()
{
var client = new DaprClientBuilder().Build();
await client.PublishEventAsync("pubsub", "order-created", new { Id = 1001 });
return Ok();
}
// 库存服务订阅处理
[Topic("pubsub", "order-created")]
[HttpPost("order-created")]
public IActionResult HandleOrder(dynamic order)
{
// 扣减库存逻辑
return Ok();
}
四、服务调用与SDK对比
Dapr支持服务间通过/v1.0/invoke/{app-id}/method/{method}路径调用。C#既可手写HttpClient访问Sidecar,也可用DaprClient.InvokeMethodAsync简化。后者自动处理序列化与错误码,推荐在内部服务使用。
下表列出两种调用方式差异:
| 方式 | 优点 | 缺点 |
|---|---|---|
| HttpClient直连Sidecar | 无额外依赖,易调试 | 需手动拼URL与序列化 |
| DaprClient SDK | 类型安全,代码简洁 | 绑定Dapr版本 |
实际项目中,若团队统一使用Dapr,SDK能显著降低样板代码。但边缘网关等轻量场景,直连HTTP更灵活。
五、本地运行与排错
用dapr run启动每个服务时指定唯一app-id与端口。例如订单服务:dapr run --app-id order-service --app-port 5000 dotnet run。Sidecar默认监听3500,日志可实时看到订阅注册与调用链。
常见错误是组件文件未放对位置或Redis未启动。可访问Sidecar的http://localhost:3500/v1.0/healthz确认健康。另外C#里若报证书错误,多是gRPC通道配置问题,可暂时用HTTP本地模式规避。
总结:C#接入Dapr的核心在于把Sidecar当作本地代理,业务代码只关心API与事件,分布式复杂性下沉到运行时。熟练后,迁移到Kubernetes也只是改部署编排,不用改代码。