前言:
本文简单介绍DotNetCore.CAP的简单使用,关于分布式事务中的“CAP原则”和“BASE理论”以及分布式事务的其他解决方案不做过多的介绍
CAP介绍
CAP是一个在分布式系统(SOA)/微服务系统(MicroService)中实现事件总线及最终一致性(分布式事务)的一个开源的C#库,具有轻量级,高性能,易使用等特点
CAP 具有Event Bus的所有功能,简化EventBus中de发布/订阅
CAP 具有消息持久化的功能,服务进行重启或者宕机不比担心消息丢失保证可靠性
CAP安装配置
NuGet安装:DotNetCore.CAP(基础支持),DotNetCore.CAP.RabbitMQ(底层消息队列),DotNetCore.CAP.SqlServer(数据库)
底层消息队列支持:支持使用 RabbitMQ,Kafka,Azure Service Bus等
数据库支持: Sql Server,MySql,PostgreSql,MongoDB
Startup:
public void ConfigureServices(IServiceCollection services) { services.AddCap(x => { var OrderDatabaseString = Configuration.GetConnectionString("OrderDatabaseString"); var rabbitMQ = Configuration.GetConnectionString("rabbitMQ"); x.UseSqlServer(OrderDatabaseString); x.UseRabbitMQ(rabbitMQ); x.FailedRetryCount = 10;//重试最高次数 x.FailedRetryInterval = 10; //重试间隔 x.FailedThresholdCallback = Failed => { /*重试次数达到上限*/ }; }); }
当项目正确配置完成启动后,系统会自动创建[cap].[Published] 和[cap].[Received]本地消息业务表
CAP的简单使用
用一个简单的项目举例,一个订单服务,一个产品服务(产品服务为集群);用户下单调用订单服务下单接口,下单接口需要调用产品服务的减库存接口
1,创建订单发送消息

public async Task<string> CreateOrder() { var orderId = DateTime.Now.ToString("yyyyMMdd") + new Random().Next(0, 100000000); var order = new Orders() { orderId = orderId, skunanme = "测试商品", skuId = 40004456666244, num = 50 }; var headers = new Dictionary<string, string>(); headers.Add("Buy", "111"); /*消息接收方名称*/ this.PublishName = Enum.ProductCheckHouseNum.ToString(); /*推送内容*/ this.PublishContent = order; /*推送Header*/ this.PublishHeader = headers; using (var connection = Db.OpenConnection(1)) { IDbTransaction transaction = null; try { transaction = connection.BeginTransaction(_capBus, false); var result = await connection.InsertAsync(order, transaction) > 0; await _capBus.PublishAsync(this.PublishName, this.PublishContent); if (result) transaction.Commit(); else transaction.Rollback(); orderId = result ? orderId : ""; } catch (Exception ex) { transaction?.Rollback(); } } return orderId; }
2,消息接收
/// <summary> /// 接收下单库存验证消息 /// </summary> [NonAction] [CapSubscribe("ProductCheckHouseNum")] public void ProductCheckHouseNum(DB.Model.Orders Order, [FromCap] CapHeader header) { if (Order != null) { } }
注意接收方的CapSubscribe("ProductCheckHouseNum")和推送方设置的名称必须一直,
3,补偿事务
某些情况下消费者需要返回值以告诉发布者执行结果,以便于发布者实施一些动作,通常情况下这属于补偿范围,以上面的项目为例,商品在接收到消息验证之后向订单服务返回执行结果
现在修改一下订单服务的推送
public async Task<string> CreateOrder() { var orderId = DateTime.Now.ToString("yyyyMMdd") + new Random().Next(0, 100000000); var order = new Orders() { orderId = orderId, skunanme = "测试商品", skuId = 40004456666244, num = 50 }; var headers = new Dictionary<string, string>(); headers.Add("Buy", "111"); /*消息接收方名称*/ this.PublishName = Enum.ProductCheckHouseNum.ToString(); /*推送内容*/ this.PublishContent = order; /*推送Header*/ this.PublishHeader = headers; using (var connection = Db.OpenConnection(1)) { IDbTransaction transaction = null; try { transaction = connection.BeginTransaction(_capBus, false); var result = await connection.InsertAsync(order, transaction) > 0; await _capBus.PublishAsync(this.PublishName, this.PublishContent, "callbackFunction"); if (result) transaction.Commit(); else transaction.Rollback(); orderId = result ? orderId : ""; } catch (Exception ex) { transaction?.Rollback(); } } return orderId; } /// <summary> /// 回调函数 /// </summary> /// <param name="order"></param> [NonAction] [CapSubscribe("callbackFunction")] public void MarkOrderStatus(DB.Model.Orders order) { if (order != null) { } }
对应的修改商品服务:
/// <summary> /// 接收下单库存验证消息 /// </summary> [NonAction] [CapSubscribe("ProductCheckHouseNum")] public async Task<Orders> ProductCheckHouseNums(DB.Model.Orders Order, [FromCap] CapHeader header) { if (Order != null) { //业务处理 } return Order; }
在CAP推送方法PublishAsync的调用时指定callbackName
来得到消费者的执行结果,注意下游服务一定要return
4,幂等性验证
幂等性指的是多次操作,结果是一致的 ,但是CAP无法保证消息不重复(即使是能保证该有的验证还是要自己弄),实际使用中需要自己考虑一下消息的重复过滤和幂等性
在上面的例子中接收方是模拟的集群,相同的消息会接收两次,相应的推送方订单服务也会接收到两次
所以为保证幂等性在商品库中建立了一个流水表,用商品Id和订单号作为联合主键保证业务处理的唯一,订单服务同理,, 当然这只是一种处理方式,也可以使用Redis等进行处理
总结
这只是CAP的简单使用,更多介绍请访问:https://cap.dotnetcore.xyz/user-guide/zh/getting-started/quick-start/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构