13.分布式事件总线DotNetCore.CAP的简单使用
DotNetCore.CAP框架提供了一个简单易用的API和多种消息传输协议支持(包括Redis、RabbitMQ等),可以让用户轻松地实现消息队列、事件发布/订阅、分布式事务等功能。它还具备自动重试、异常处理、数据序列化等高级特性,可以保证消息的可靠性和一致性。
使用DotNetCore.CAP框架,你可以:
1. 发布并订阅消息,支持同步和异步模式;
2. 实现分布式事务管理,确保各个步骤的一致性;
3. 建立多种消息传输协议,如Kafka、RabbitMQ等;
4. 支持多种数据库驱动程序,如MySql、Oracle等;
5. 灵活采用自定义消息格式,以满足你的需求。
除此之外,该框架还具有良好的可扩展性,支持在集群环境下进行部署,并与其他.NET Core框架(如Entity Framework Core)完美配合使用。
安装包:
DotNetCore.CAP
DotNetCore.CAP.Dashboard
DotNetCore.CAP.MySql
DotNetCore.CAP.RabbitMQ
Microsoft.AspNetCore.OpenApi
Newtonsoft.Json
Pomelo.EntityFrameworkCore.MySql
Swashbuckle.AspNetCore
添加一个项目EventBus.DotnetCap(http://localhost:5284)
appsettings.Development.json配置
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "RabbitMQ": { "HostName": "127.0.0.1", "VirtualHost": "/", "UserName": "admin", "Password": "你的密码", "Port": "5672" }, "ConnectionStrings": { "mysql": "server=127.0.0.1;uid=root;pwd=123456;database=DotnetCoreCap" } }
安装RabbitMQ
1. 下载带web管理客户端的镜像:
docker pull rabbitmq:management
2. 创建容器并运行(15672是管理界面的端口,5672是服务的端口。这里顺便将管理系统的用户名和密码设置为admin admin)
docker run -d --name=rabbit --net=happy_travel --restart=always -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=123456 -p 15672:15672 -p 5672:5672 rabbitmq:management
3. 若未设置,RABBITMQ_DEFAULT_USER,RABBITMQ_DEFAULT_PASS 时,默认账号密码都为:admin
4. 打开浏览器,输入网址:http://localhost:15672 ,输入账号与密码即可登录rabbitmq 客户端管理界面
添加一个EF链接Mysql的上下文MyDbContextContext.cs
public class MyDbContextContext:DbContext { public MyDbContextContext(DbContextOptions<MyDbContextContext> options) : base(options) { } public DbSet<UserInfo> UserInfo { get; set; } }
Program.cs配置
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext<MyDbContextContext>(p => { p.UseMySql(builder.Configuration.GetConnectionString("mysql"),new MySqlServerVersion("5.7")); }); builder.Services.AddTransient<ReceiveService>(); // 配置事件总线以及分布式事务 var rabbitConfig = builder.Configuration.GetSection("RabbitMQ"); builder.Services.Configure<RabbitMQOptions>(rabbitConfig); var rabbitOptions = rabbitConfig.Get<RabbitMQOptions>(); builder.Services.AddCap(p => { // 在数据库中会生成cap.receive 与 cap.publish 表 // 同时也支持分布式事务 p.UseMySql(builder.Configuration.GetConnectionString("mysql") ?? string.Empty); p.UseEntityFramework<MyDbContextContext>(); p.UseRabbitMQ(mq => { mq.HostName = rabbitOptions.HostName; mq.VirtualHost = rabbitOptions.VirtualHost; mq.UserName = rabbitOptions.UserName; mq.Password = rabbitOptions.Password; mq.Port = rabbitOptions.Port; }); p.UseDashboard(); // 注册仪表盘 // 仪表盘默认的访问地址是:http://localhost:xxx/cap,你可以在d.MatchPath配置项中修改cap路径后缀为其他的名字。 //设置处理成功的数据在数据库中保存的时间(秒),为保证系统新能,数据会定期清理。 p.SucceedMessageExpiredAfter = 24 * 3600; //设置失败重试次数 p.FailedRetryCount = 5; }); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
添加一个控制器HomeController( 发布 )
[Route("[controller]/[action]")] [ApiController] public class HomeController:ControllerBase { private readonly ICapPublisher _capPublisher; public HomeController(ICapPublisher capPublisher) { _capPublisher = capPublisher; } [HttpGet] public IActionResult TestPub() { _capPublisher.Publish("eventbus_Key01",new UserInfo{Id = 1,NickName = "张三"}); return Ok("消息发布成功,请等待处理结果"); }
//在EventBus.DotnetCoreCap.Subscribe项目中接收 [HttpGet] public IActionResult TestSubOnOtherProject() { _capPublisher.Publish("eventbus_Key02",new UserInfo{Id = 2,NickName = "李四"}); return Ok("消息发布成功,请等待处理结果"); } }
添加一个控制器ReceiveController( 接收、订阅 )
[Route("[controller]/[action]")] [ApiController] public class ReceiveController:ControllerBase { [NonAction] [CapSubscribe("eventbus_Key01",Group = "eventbus_Key01")] public void CheckReceive(UserInfo info) { var json = JsonConvert.SerializeObject(info); Console.WriteLine(json); } }
或者
public class ReceiveService:ICapSubscribe { [CapSubscribe("eventbus_Key01",Group = "eventbus_Key01")] public void CheckReceive(UserInfo info) { var json = JsonConvert.SerializeObject(info); Console.WriteLine(json); } }
事务:
[Route("[controller]/[action]")] [ApiController] public class TransactionController:ControllerBase { private readonly ICapPublisher _cap; private readonly MyDbContextContext _context; public TransactionController(ICapPublisher cap, MyDbContextContext context) { _cap = cap; _context = context; } // 原理:拆分成多个本地事务来看待 // 场景1:正常事务消息发布 [HttpGet] public IActionResult Publish1() { var tran = _context.Database.BeginTransaction(_cap); try { _context.UserInfo.Add(new() {NickName = "张三"}); _context.SaveChanges(); _cap.Publish("tran.cap","李四"); tran.Commit(); return Ok("成功"); } catch (Exception e) { tran.Rollback(); return Ok("失败"); } } // 场景2:发布者发生异常, 消费者根本就收不到消息 [HttpGet] public IActionResult Publish2() { var tran = _context.Database.BeginTransaction(_cap); try { _context.UserInfo.Add(new() {NickName = "张三2"}); _context.SaveChanges(); _cap.Publish("tran.cap2","李四2"); throw new Exception("测试发布者出异常"); tran.Commit(); return Ok("成功"); } catch (Exception e) { tran.Rollback(); return Ok("失败"); } } // 场景3: 发布者正常,消费者发生异常,不会导致发布者回滚 [HttpGet] public IActionResult Publish3() { var tran = _context.Database.BeginTransaction(_cap); try { _context.UserInfo.Add(new() {NickName = "张三3"}); _context.SaveChanges(); _cap.Publish("tran.cap3","李四3"); tran.Commit(); return Ok("成功"); } catch (Exception e) { tran.Rollback(); return Ok("失败"); } } }
添加另一个项目EventBus.DotnetCoreCap.Subscribe(http://localhost:5187)
appsettings.Development.json配置
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "RabbitMQ": { "HostName": "127.0.0.1", "VirtualHost": "/", "UserName": "admin", "Password": "你的密码", "Port": "5672" }, "ConnectionStrings": { "mysql": "server=127.0.0.1;uid=root;pwd=123456;database=DotnetCoreCap" } }
public class MyDbContextContext:DbContext { public MyDbContextContext(DbContextOptions<MyDbContextContext> options) : base(options) { } public DbSet<UserInfo> UserInfo { get; set; } }
Program.cs配置
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext<MyDbContextContext>(p => { p.UseMySql(builder.Configuration.GetConnectionString("mysql"),new MySqlServerVersion("5.7")); }); var rabbitConfig = builder.Configuration.GetSection("RabbitMQ"); builder.Services.Configure<RabbitMQOptions>(rabbitConfig); var rabbitOptions = rabbitConfig.Get<RabbitMQOptions>(); builder.Services.AddCap(p => { // 在数据库中会生成cap.receive 与 cap.publish 表 p.UseMySql(builder.Configuration.GetConnectionString("mysql") ?? string.Empty); //p.UseEntityFramework<MyDbContextContext>(); p.UseRabbitMQ(mq => { mq.HostName = rabbitOptions.HostName; mq.VirtualHost = rabbitOptions.VirtualHost; mq.UserName = rabbitOptions.UserName; mq.Password = rabbitOptions.Password; mq.Port = rabbitOptions.Port; }); p.UseDashboard(); // 注册仪表盘 // 仪表盘默认的访问地址是:http://localhost:xxx/cap,你可以在d.MatchPath配置项中修改cap路径后缀为其他的名字。 //设置处理成功的数据在数据库中保存的时间(秒),为保证系统新能,数据会定期清理。 p.SucceedMessageExpiredAfter = 24 * 3600; //设置失败重试次数 p.FailedRetryCount = 5; }); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
添加一个控制器SubController用于订阅和接收
[Route("[controller]/[action]")] [ApiController] public class SubController:ControllerBase { [NonAction] [CapSubscribe("eventbus_Key02",Group = "eventbus_Key02")] public void Receive(UserInfo info) { var json = JsonConvert.SerializeObject(info); Console.WriteLine(json); } [NonAction] [CapSubscribe("tran.cap",Group = "tran.cap")] public void ReceiveTran1(string nickName) { Console.WriteLine(nickName); } [NonAction] [CapSubscribe("tran.cap2",Group = "tran.cap2")] public void ReceiveTran2(string nickName) { Console.WriteLine(nickName); } [NonAction] [CapSubscribe("tran.cap3",Group = "tran.cap3")] public void ReceiveTran3(string nickName) { Console.WriteLine(nickName); // throw new Exception("消费者异常"); } }