ASP.NET Core CAP 分布式事务

GitHub - dotnetcore/CAP  
快速开始 - CAP
eShopOnContainers  

CAP 是一个基于 .NET Standard 的 C# 库,它是一种处理分布式事务的解决方案,同样具有 EventBus 的功能,它具有轻量级、易使用、高性能等特点。
CAP 具有 Event Bus 的所有功能,并且CAP提供了更加简化的方式来处理EventBus中的发布/订阅。
CAP 具有消息持久化的功能,也就是当你的服务进行重启或者宕机时,她可以保证消息的可靠性。
CAP 实现了分布式事务中的最终一致性,你不用再去处理这些琐碎的细节。
CAP 提供了基于 Microsoft DI 的 API 服务,她可以和你的 ASP.NET Core 系统进行无缝结合,并且能够和你的业务代码集成支持强一致性的事务处理。
CAP 是开源免费的。CAP基于MIT协议开源,你可以免费的在你的私人或者商业项目中使用,不会有人向你收取任何费用。
CAP 支持 Kafka、RabbitMQ、AzureServiceBus、AmazonSQS 等消息队列。
CAP 提供了 Sql Server, MySql, PostgreSQL,MongoDB 的扩展作为数据库存储:
发布事件/消息
订阅事件/消息

数据库表名称
cap.lock
cap.published
cap.received

1、管理 NuGet 程序包

DotNetCore.CAP
DotNetCore.CAP.RabbitMQ
DotNetCore.CAP.Dashboard
DotNetCore.CAP.SqlServer
DotNetCore.CAP.MySql
DotNetCore.CAP.PostgreSql
DotNetCore.CAP.Kafka
DotNetCore.CAP.MongoDB
DotNetCore.CAP.Dashboard.K8s

2、Program.cs中使用

using DotNetCore.CAP.Dashboard.NodeDiscovery;

builder.Services.AddCap(options =>
{
    options.UseMySql(builder.Configuration.GetConnectionString("MySqlDefault"));
    options.UseDashboard();
    options.UseRabbitMQ(rabbitMq =>
    {
        rabbitMq.HostName = builder.Configuration["CAP:RabbitMq:HostName"];
        rabbitMq.Port = Convert.ToInt32(builder.Configuration["CAP:RabbitMq:Port"]);
        rabbitMq.UserName = builder.Configuration["CAP:RabbitMq:UserName"];
        rabbitMq.Password = builder.Configuration["CAP:RabbitMq:Password"];
    });
    options.UseDashboard();

    #region .NET Core 3.0
    DiscoveryOptions discoveryOptions = new DiscoveryOptions();
    discoveryOptions.CurrentNodePort = 5173;
    builder.Configuration.Bind(discoveryOptions);

    options.UseDiscovery(d =>
    {
        d.DiscoveryServerHostName = "localhost";
        d.DiscoveryServerPort = 8500;
        d.CurrentNodeHostName = "localhost";
        d.CurrentNodePort = 5222;
        d.NodeId = "1";
        d.NodeName = "fanlin";
        d.Scheme = "http";
        d.MatchPath = "/api/HealthCheck";
    });
    #endregion

    #region .NET 6.0
    ConsulDiscoveryOptions discoveryOptions = new ConsulDiscoveryOptions();
    discoveryOptions.CurrentNodePort = 5173;
    builder.Configuration.Bind(discoveryOptions);

    options.UseConsulDiscovery(d =>
    {
        d.DiscoveryServerHostName = "localhost";
        d.DiscoveryServerPort = 8500;
        d.CurrentNodeHostName = "localhost";
        d.CurrentNodePort = 5222;
        d.NodeId = "1";
        d.NodeName = "fanlin";
        d.Scheme = "http";
        d.MatchPath = "/api/HealthCheck";
    });
    #endregion

    //失败后的重试次数,默认50次
    options.FailedRetryCount = 2;
    //失败后的重试间隔,默认60秒
    options.FailedRetryInterval = 5;
    //设置处理成功的数据在数据库中保存的时间(秒),数据会定期删除,默认24*3600秒
    options.SucceedMessageExpiredAfter = 24 * 3600;

    options.FailedThresholdCallback = info =>
    {
        Console.WriteLine("Publish Message Error::" + info.Message);
    };
}).AddSubscribeFilter<MyCapFilter>();

SetupCap.cs,过滤器

using DotNetCore.CAP.Filter;

namespace Trial.WebAPI.Extensions
{
    public static class SetupCap
    {
        public static void AddCapSetup(this IServiceCollection services)
        {
            if (services == null) throw new ArgumentNullException(nameof(services));

            // services.AddTransient<ISubscriberService, SubscriberService>();

            services.AddCap(x =>
            {
                //如果你使用的ADO.NET,根据数据库选择进行配置:
                x.UseMySql(AppSettings.Configuration["Default"]);

                //CAP支持 RabbitMQ、Kafka、AzureServiceBus 等作为MQ,根据使用选择配置:
                x.UseRabbitMQ(o =>
                {
                    o.HostName = AppSettings.Configuration["CAP:RabbitMQ:HostName"];
                    o.UserName = AppSettings.Configuration["CAP:RabbitMQ:UserName"];
                    o.Password = AppSettings.Configuration["CAP:RabbitMQ:Password"];
                    o.VirtualHost = AppSettings.Configuration["CAP:RabbitMQ:VirtualHost"];
                    o.Port = Convert.ToInt32(AppSettings.Configuration["CAP:RabbitMQ:Port"]);
                });

                //设置处理成功的数据在数据库中保存的时间(秒),为保证系统新能,数据会定期清理。
                x.SucceedMessageExpiredAfter = 24 * 3600;

                //设置失败重试次数
                x.FailedRetryCount = 100;

                //设置失败重试间隔
                x.FailedRetryInterval = 20 * 60;

                // 注册 Dashboard
                x.UseDashboard();
            }).AddSubscribeFilter<MyCapFilter>();
        }

        public static void UseCapSetup(this IApplicationBuilder app)
        {
        }
    }

    public class MyCapFilter : SubscribeFilter
    {
        public override Task OnSubscribeExecutingAsync(ExecutingContext context)
        {
            // 订阅方法执行前
            return Task.CompletedTask;
        }

        public override Task OnSubscribeExecutedAsync(ExecutedContext context)
        {
            // 订阅方法执行后
            return Task.CompletedTask;
        }

        public override Task OnSubscribeExceptionAsync(ExceptionContext context)
        {
            // 订阅方法执行异常
            return Task.CompletedTask;
        }
    }
}

3、CapPublishController.cs【发送消息,发送延迟消息,发送包含头信息的消息】

using DotNetCore.CAP;
using Microsoft.AspNetCore.Mvc;
using Trial.Repository.CarCorrectModify;

namespace Trial.WebAPI.Controllers
{
    [Route("api/CapPublish/[action]")]
    [ApiController]
    public class CapPublishController : ControllerBase
    {
        private static string publishName = "CAPTest.Test";

        private readonly ICapPublisher _capPublisher;
        private readonly ICarCorrectModifyRepository _repository;
        private readonly IConfiguration _configuration;

        /// <summary>
        /// 
        /// </summary>
        public CapPublishController(ICapPublisher capPublisher, ICarCorrectModifyRepository repository, IConfiguration configuration)
        {
            _capPublisher = capPublisher;
            _repository = repository;
            _configuration = configuration;
        }

        /// <summary>
        /// 发送消息,发送包含头信息的消息
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> Publish()
        {
            IDictionary<string, string?> dictHeader = new Dictionary<string, string?>();
            dictHeader.Add("name", "zhangsan");
            dictHeader.Add("sex", "男");
            dictHeader.Add("age", "18");

            await _capPublisher.PublishAsync(publishName, DateTime.Now, dictHeader);
            return Ok();
        }

        /// <summary>
        /// 发送延迟消息,发送包含头信息的消息
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> SendDelayMessage()
        {
            IDictionary<string, string?> dictHeader = new Dictionary<string, string?>()
            {
                ["my.header.first"] = "first",
                ["my.header.second"] = "second"
            };

            dictHeader.Add("name", "zhangsan");
            dictHeader.Add("sex", "男");
            dictHeader.Add("age", "18");

            await _capPublisher.PublishDelayAsync(TimeSpan.FromSeconds(100), "test.show.time", DateTime.Now, dictHeader);

            return Ok();
        }
    }
}

4、CapConsumerController.cs【处理消息,处理包含头信息的消息】

using DotNetCore.CAP;
using Microsoft.AspNetCore.Mvc;
using Trial.Repository.CarCorrectModify;

namespace Trial.WebAPI.Controllers
{
    [Route("api/CapConsumer/[action]")]
    [ApiController]
    public class CapConsumerController : ControllerBase
    {
        /// <summary>
        /// 
        /// </summary>
        public CapConsumerController()
        { }

        /// <summary>
        /// 订阅
        /// </summary>
        /// <param name="data"></param>
        [NonAction]
        [CapSubscribe("CAPTest.Test")]
        public void Distributed(string data, [FromCap] CapHeader header)
        {
            string name = header["name"];
            string message = $"接收到订阅:{data}";
            //to do something ...
        }

        /// <summary>
        /// 订阅
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [NonAction]
        [CapSubscribe("CAPDemo.Test1", Group = "CAPDemo.Test1.Main")]
        public void Receive(string data, [FromCap] CapHeader header)
        {
            string name = header["name"];
            string message = $"接收到订阅:{data}";
            //to do something ...
        }
    }
}

5、appsettings.json【注意:修改本地 RabbitMq 密码】

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MySqlDefault": "Data Source=localhost;Port=3306;Database=dbCap;uid=root;pwd=1q2w3E*;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true;MinimumPoolSize=50;MaximumPoolSize=1000",
    "SqlServerDefault": "data source=.;initial catalog=dbCap;user id=sa;password=000000;"
  },
  "Cap": {
    "Enabled": "true",
    "RabbitMq": {
      "HostName": "localhost",
      "Port": 5672,
      "UserName": "admin",
      "Password": "admin",
      "VirtualHost": "vhost.adnc.dev"
    }
  }
}

6、打开CAP页面,可以看到各种事件和状态
http://localhost:5090/cap/index.html#/
*
*
*

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值