(4)最终通过比较和研究,引入DonetCore.CAP来解决分布式系统的最终一致性和代码的解耦 二.项目实战 CAP文档:https://cap.dotnetcore.xyz/user-guide/zh/getting-started/quick-start/ 项目基于.Net Core3.1,消息队列RabbitMQ,数据库Postgres (1)创建项目CAPSolution CAPTest作为用户中心微服务 CAPTest2作为角色权限微服务 CAPTest发布创建角色的事件,给CAPTest2订阅,维护角色权限关联表 流程图大致如下:
(2)创建实体表,迁移数据库 CAPTest中创建角色表Role
[Table("Role")] public class Role { [Key] [Column("id")] [StringLength(50)] public string Id { get; set; } = Guid.NewGuid().ToString(); public string RoleId { get; set; } public string RoleName { get; set; } }CAPTest2中创建Role_Permission
[Table("Role_Permission")] public class Role_Permission { [Key] [Column("id")] [StringLength(50)] public string Id { get; set; } = Guid.NewGuid().ToString(); public string RoleId { get; set; } public string PermissioId { get; set; } }迁移数据库 在CAPTest和CAPTest2中添加以下Nuget包: 修改appsettings.json
"ConnectionStrings": { "ConnectionString": "User ID=postgres;Password=c2matica;Host=192.168.1.230;Port=5432;Database=Craftica;Pooling=true;" }修改Startup.cs
1 #region DB 2 var sqlConnectionString = Configuration.GetConnectionString("ConnectionString"); 3 services.AddDbContext<CAPRoleDbContext>(options => 4 { 5 options.UseLazyLoadingProxies().UseNpgsql(sqlConnectionString); 6 }); 7 #endregion执行迁移命令 (2)业务代码 引入CAP的Nuget相关包 配置CAP的相关属性
1 services.AddCap(x => 2 { 3 x.UseEntityFramework<CAPRoleDbContext>(); 4 //使用postgresql进行事务处理,防止推送MQ失败,会在指定数据库中自动生成以"cap."开头的表 5 x.UsePostgreSql(Configuration.GetConnectionString("ConnectionString")); 6 //x.UseInMemoryMessageQueue(); 7 x.UseRabbitMQ(rb => 8 { 9 rb.HostName = "localhost"; 10 rb.UserName = "guest"; 11 rb.Password = "guest"; 12 rb.Port = 5672; 13 rb.ExchangeName = "cap.text.exchange"; 14 }); 15 // 添加cap后台监控页面(人工处理);页面地址为“/cap”;如:http://www.site.com/cap 16 //x.UseDashboard(); 17 // 配置定时器重试策略 18 //x.FailedRetryInterval = 2; //重试间隔时间(秒),使用默认的就可以,可不用配置 19 x.FailedRetryCount = 5; //重试次数 20 });在CAPTest中创建RoleController,创建方法AddRole,依赖注入ICapPublisher
1 private readonly CAPRoleDbContext _cAPRoleDbContext; 2 protected readonly ICapPublisher _capBus; 3 public RoleController(CAPRoleDbContext cAPRoleDbContext, ICapPublisher capBus) 4 { 5 _cAPRoleDbContext = cAPRoleDbContext; 6 _capBus = capBus; 7 } 8 [HttpPost("addrole")] 9 public async Task<IActionResult> AddRole(string roleid) 10 { 11 _cAPRoleDbContext.Role.Add(new Role() 12 { 13 RoleId = roleid, 14 RoleName = "Name" 15 }); 16 var data= await _cAPRoleDbContext.SaveChangesAsync()>0; 17 18 if (data) 19 { 20 await _capBus.PublishAsync("role.service.addrole", new RolePermissionDto() 21 { 22 RoleId = roleid 23 }); 24 } 25 return Ok(); 26 }在CAPTest2中创建PermissionController, 创建方法AddRolePermission,依赖注入ICapPublisher
1 public class PermissionController : Controller 2 { 3 private readonly CAPPermissionDBContext _cAPPermissionDbContext; 4 protected readonly ICapPublisher _capBus; 5 public PermissionController(CAPPermissionDBContext cAPPermissionDbContext, ICapPublisher capBus) 6 { 7 _cAPPermissionDbContext = cAPPermissionDbContext; 8 _capBus = capBus; 9 } 10 11 [NonAction] 12 [CapSubscribe("role.service.addrole")] 13 public async Task<IActionResult> AddRolePermission(RolePermissionDto model) 14 { 15 _cAPPermissionDbContext.Role_Permission.Add(new Role_Permission() 16 { 17 PermissioId = "1", 18 RoleId = model.RoleId 19 }); 20 await _cAPPermissionDbContext.SaveChangesAsync(); 21 return Ok(); 22 } 23 }订阅的方法,要在控制器头部加上标签: [NonAction] [CapSubscribe("role.service.addrole")] (3)程序运行 首先启动RabbitMQ服务 其次运行微服务,看到CAP started!时表示运行成功 调用CAPTest中AddRole方法,在微服务CAPTest2中会运行对应的方法AddRolePermission。 完成后,可以在表Role,表Role_Permission中看到新增的记录,如下图所示 Role Role_Permission
在各自数据库中,会对CAP的发布和订阅记录进行存储 数据库CAP_Permission中 cap的received会有记录 数据库CAP_Role中 cap的published会有记录
注:cap.published表和cap.received表是由CAP自动生成的,它内部是使用本地消息表+MQ来实现异步确保
若只启动发布(CAPTest),看到消息队列下中Exchange为“cap.text.exchange”的一条待消费的记录
以上仅用于学习和总结! 参考文档: https://www.cnblogs.com/savorboard/p/base-an-acid-alternative.html https://www.cnblogs.com/xhznl/p/13154851.html https://baike.baidu.com/item/CAP%E5%8E%9F%E5%88%99 附: 链接:https://pan.baidu.com/s/1GpmgF0Px5yPTsfkSAwhiYw 提取码:kw89