官网:https://cap.dotnetcore.xyz

相关介绍

CAP 是一个EventBus,同时也是一个在微服务或者SOA系统中解决分布式事务问题的一个框架。它有助于创建可扩展,可靠并且易于更改的微服务系统。

在微软的 eShop 微服务示例项目中,推荐使用 CAP 作为生产环境可用的 EventBus。

什么是 EventBus?

事件总线是一种机制,它允许不同的组件彼此通信而不彼此了解。 组件可以将事件发送到Eventbus,而无需知道是谁来接听或有多少其他人来接听。 组件也可以侦听Eventbus上的事件,而无需知道谁发送了事件。 这样,组件可以相互通信而无需相互依赖。 同样,很容易替换一个组件。 只要新组件了解正在发送和接收的事件,其他组件就永远不会知道.

相对于其他的 Service Bus 或者 Event Bus, CAP 拥有自己的特色,它不要求使用者发送消息或者处理消息的时候实现或者继承任何接口,拥有非常高的灵活性。我们一直坚信约定大于配置,所以CAP使用起来非常简单,对于新手非常友好,并且拥有轻量级。

CAP 采用模块化设计,具有高度的可扩展性。你有许多选项可以选择,包括消息队列,存储,序列化方式等,系统的许多元素内容可以替换为自定义实现。

简单实战

使用CAP需要依赖的第三方库,这里我们持久化选用了SqlServer数据库,消息队列使用了Rabbitmq

相关配置(上游系统、下游系统配置相同)

// 注入EF上下文对象
var CapConnectionString = builder.Configuration.GetSection("CAPConnectionStrings").GetValue<string>("DefaultConnection");
builder.Services.AddDbContext<CAPDbContext>(options =>
options.UseSqlServer(CapConnectionString));
//注入CAP
builder.Services.AddCap(x =>
{
// 使用RabbitMQ作为消息队列
x.UseRabbitMQ(opt =>
{
opt.HostName = "192.168.3.128";
opt.Port = 5674;
opt.UserName = "guest";
opt.Password = "guest";
opt.VirtualHost = "/";
});
// 使用SqlServer作为CAP的存储
x.UseSqlServer(opt =>
{
opt.ConnectionString = CapConnectionString;
});
// 设置CAP的其他选项
x.FailedRetryCount = 5; // 失败重试次数
x.FailedRetryInterval = 60; // 失败重试间隔(秒)
x.UseDashboard(dashoptions =>
{
dashoptions.PathMatch = "/cap"; //面板地址
});
});

上游系统发布

 /// <summary>
/// CAP:基于消息队列通过事件驱动+回调方法补偿的机制实现分布式事务的最终一致性
/// 1、消息被消费者至少接收到一次,需要通过接口幂等性/redis唯一ID实现消息不会重复消费
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class CAPController : ControllerBase
{
private readonly ICapPublisher capPublisher;
private readonly CAPDbContext cAPDbContext;
public CAPController(ICapPublisher capPublisher, CAPDbContext cAPDbContext)
{
this.capPublisher = capPublisher;
this.cAPDbContext = cAPDbContext;
} [HttpGet("CreateOrder")]
public IActionResult CreateOrder()
{
using (var trans = cAPDbContext.Database.BeginTransaction(capPublisher, autoCommit: true))
{
//业务代码
try
{
var order = new OrderInfo { OrderId = Guid.NewGuid().ToString(), ProId= "Pro_13327530-e706-4ef3-aa5b-02aac9227499", Status = 0 };
cAPDbContext.orderInfos.Add(order);
capPublisher.Publish("test_createorder_v1", order, "test_createorder_callback_v1");
cAPDbContext.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
return Ok();
}
[CapSubscribe("test_createorder_callback_v1")]
private void CreateOrderCallback(JsonElement param)
{
var isSuccess = param.GetProperty("IsSuccess").GetBoolean();
var OrderId = param.GetProperty("OrderId").GetString();
if (isSuccess)
{
var order = cAPDbContext.orderInfos.FirstOrDefault(c => c.OrderId == OrderId);
order.Status = 1;
cAPDbContext.Update(order);
cAPDbContext.SaveChanges();
Console.WriteLine("修改订单状态成功");
}
else
{
var order = cAPDbContext.orderInfos.FirstOrDefault(c => c.OrderId == OrderId);
order.Status = -1;
cAPDbContext.Update(order);
cAPDbContext.SaveChanges();
Console.WriteLine("下单失败,执行补偿");
}
}
}

下游订阅处理

[Route("api/[controller]")]
[ApiController]
public class CapConsumerController : ControllerBase
{
private readonly ICapPublisher capPublisher;
private readonly CAPDbContext cAPDbContext;
public CapConsumerController(ICapPublisher capPublisher, CAPDbContext cAPDbContext)
{
this.capPublisher = capPublisher;
this.cAPDbContext = cAPDbContext;
} [HttpGet("CreatePro")]
public IActionResult CreatePro()
{
cAPDbContext.proInfos.Add(new ProInfo { ProId = "Pro_" + Guid.NewGuid().ToString(), Count = 10, ProName = "商品1" });
cAPDbContext.proInfos.Add(new ProInfo { ProId = "Pro_" + Guid.NewGuid().ToString(), Count = 10, ProName = "商品2" });
cAPDbContext.proInfos.Add(new ProInfo { ProId = "Pro_" + Guid.NewGuid().ToString(), Count = 10, ProName = "商品3" });
cAPDbContext.proInfos.Add(new ProInfo { ProId = "Pro_" + Guid.NewGuid().ToString(), Count = 10, ProName = "商品4" });
cAPDbContext.SaveChanges();
return Ok();
} [CapSubscribe("test_createorder_v1")]
private object KjProCount(JsonElement param)
{
var obj = param.Deserialize<OrderInfo>();
try
{
if (obj != null)
{
using (var trans = cAPDbContext.Database.BeginTransaction(capPublisher, autoCommit: true))
{
var pro = cAPDbContext.proInfos.FirstOrDefault(c => c.ProId == obj.ProId);
if (pro != null && pro.Count > 0)
{
pro.Count--;
cAPDbContext.Update(pro);
cAPDbContext.proOrderRecords.Add(new ProOrderRecord { RecordId = Guid.NewGuid().ToString(), OrderId = obj.OrderId, ProId = pro.ProId });
cAPDbContext.SaveChanges();
throw new Exception("Error");
Console.WriteLine($"下单成功{JsonConvert.SerializeObject(obj)}");
return new { IsSuccess = true, OrderId = obj.OrderId };
}
else
{
Console.WriteLine($"库存不足{JsonConvert.SerializeObject(obj)}");
}
}
}
return new { IsSuccess = false, OrderId = obj.OrderId };
}
catch (Exception ex)
{
Console.WriteLine("发生异常回滚");
return new { IsSuccess = false, OrderId = obj.OrderId };
}
}
}

在上游和下游系统中会自动创建2个表(发布、接收的消息)

监控界面

基于donetcore/CAP实现分布式事务一致性的更多相关文章

  1. .Net Core with 微服务 - 使用 AgileDT 快速实现基于可靠消息的分布式事务

    前面对于分布式事务也讲了好几篇了(可靠消息最终一致性 分布式事务 - TCC 分布式事务 - 2PC.3PC),但是还没有实战过.那么本篇我们就来演示下如何在 .NET 环境下实现一个基于可靠消息的分 ...

  2. 微服务痛点-基于Dubbo + Seata的分布式事务(AT)模式

    前言 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata 将为用户提供了 AT.TCC.SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案. ...

  3. 微服务痛点-基于Dubbo + Seata的分布式事务(TCC模式)

    前言 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata 将为用户提供了 AT.TCC.SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案. ...

  4. Asp.Net Core&CAP实现分布式事务

    需要注意的是标题中的CAP不是指的CAP理论,而是园区大神杨晓东实现的框架,CAP框架基于本地消息表用最终一致性实现分布式事务. 本地消息表 首先我们考虑一个场景,在将用户信息更改后,需要发送一条消息 ...

  5. 三:分布式事务一致性协议2pc和3pc

    一:分布式一致性协议--->对于一个分布式系统进行架构设计的过程中,往往会在系统的可用性和数据一致性之间进行反复的权衡,于是就产生了一系列的一致性协议.--->长期探索涌现出一大批经典的一 ...

  6. 六:分布式事务一致性协议paxos的分析

    最近研究paxos算法,看了许多相关的文章,概念还是很模糊,觉得还是没有掌握paxos算法的精髓,所以花了3天时间分析了libpaxos3的所有代码,此代码可以从https://bitbucket.o ...

  7. Dubbo 分布式事务一致性实现

    我觉得事务的管理不应该属于Dubbo框架, Dubbo只需实现可被事务管理即可, 像JDBC和JMS都是可被事务管理的分布式资源, Dubbo只要实现相同的可被事务管理的行为,比如可以回滚, 其它事务 ...

  8. 【原】ActiveMq实现分布式事务一致性

    前言:关于分布式事务话题一直是颇有争议的话题,在本文中通过ActiveMq 实现分布式事务做一个简单的demo;同时也让自己能在实践中可以获取经验和对分布式事务自己的一些思考. 1.本地事务 我们通常 ...

  9. MySQL 中基于 XA 实现的分布式事务

    1 XA协议 首先我们来简要看下分布式事务处理的XA规范可知XA规范中分布式事务有AP,RM,TM组成: 其中应用程序(Application Program ,简称AP):AP定义事务边界(定义事务 ...

  10. 四:分布式事务一致性协议paxos通俗理解

    转载地址:http://www.lxway.com/4618606.htm 维基的简介:Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 LaTeX 中的"La" ...

随机推荐

  1. PetaLinux常用命令汇总

    创建petalinux工程 Create a new project from a reference BSP file. 用于从官方下载的BSP中抽取数据产生工程. petalinux-create ...

  2. Java基础:线程的三种创建方式

    一.继承Thread类 定义一个类继承线程类Thread 重写run()方法 创建线程对象 调用线程对象的start()方法创建线程 Thread类的常用API setName(String name ...

  3. 可视化学习:如何用WebGL绘制3D物体

    在之前的文章中,我们使用WebGL绘制了很多二维的图形和图像,在学习2D绘图的时候,我们提过很多次关于GPU的高效渲染,但是2D图形的绘制只展示了WebGL部分的能力,WebGL更强大的地方在于,它可 ...

  4. 使用libzip压缩文件和文件夹

    简单说说自己遇到的坑: 分清楚三个组件:zlib.minizip和libzip.zlib是底层和最基础的C库,用于使用Deflate算法压缩和解压缩文件流或者单个文件,但是如果要压缩文件夹就很麻烦,主 ...

  5. Python生成PDF:Reportlab的六种使用方式

    Reportlab是Python创建PDF文档的功能库 这里是整理过的六种Reportlab使用方式,主要参考的是<ReportLab User Guide> 一.使用文档模板DocTem ...

  6. 一种优秀的虚拟机内存架构 - AQ

    源链接:https://www.axa6.com/zh/an-excellent-virtual-machine-memory-architecture 简介 虚拟机内存架构直接影响虚拟机的性能和占用 ...

  7. 青少年CTF擂台挑战赛 2024 #Round 1

    青少年CTF擂台挑战赛 2024 #Round 1 crypto 1.解个方程 题目: 欢迎来到青少年CTF,领取你的题目,进行解答吧!这是一道数学题!! p = 474356125652182661 ...

  8. 前端使用 Konva 实现可视化设计器(19)- 连接线 - 直线、折线

    本章响应小伙伴的反馈,除了算法自动画连接线(仍需优化完善),实现了可以手动绘制直线.折线连接线功能. 请大家动动小手,给我一个免费的 Star 吧~ 大家如果发现了 Bug,欢迎来提 Issue 哟~ ...

  9. 【Python】Django学习1

    按黑马程序员的美多商场作方向: https://www.bilibili.com/video/BV1nf4y1k7G3 一.应用创建.注册处理.配置 Pycharm 创建Django项目: 自应用注册 ...

  10. 【Java-GUI】05 绘图 Part1

    案例: package cn.dzz; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.Acti ...