學習筆記----分佈式事務CAP簡單使用

前言

本文簡單介紹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;
        }
View Code

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/

   

 

  
   
   

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章