日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

分布式事务最终一致性-CAP框架轻松搞定

發(fā)布時(shí)間:2023/12/4 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分布式事务最终一致性-CAP框架轻松搞定 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

對(duì)于分布式事務(wù),常用的解決方案根據(jù)一致性的程度可以進(jìn)行如下劃分:

  • 強(qiáng)一致性(2PC、3PC):數(shù)據(jù)庫(kù)層面的實(shí)現(xiàn),通過鎖定資源,犧牲可用性,保證數(shù)據(jù)的強(qiáng)一致性,效率相對(duì)比較低。

  • 弱一致性(TCC):業(yè)務(wù)層面的實(shí)現(xiàn),通過預(yù)留或鎖定部分資源,最后通過確認(rèn)或取消操作完成事務(wù)的處理。比如A向B轉(zhuǎn)款500元,A賬號(hào)會(huì)凍結(jié)500元,其他操作正常,B接收轉(zhuǎn)款時(shí),也不能直接入賬,而是將500元放到預(yù)留空間,只有經(jīng)過確認(rèn)之后,A才正式扣錢,B才正式入賬;如果取消把A的500塊解凍,B也不會(huì)入賬。

  • 最終一致性(本地消息表):不管經(jīng)過多少個(gè)服務(wù)節(jié)點(diǎn),最終數(shù)據(jù)一致就行。比如下單成功之后,需要庫(kù)存服務(wù)扣減庫(kù)存,如果庫(kù)存扣減失敗,不管是重試,還是最后人工處理,最后確保訂單和庫(kù)存數(shù)據(jù)能對(duì)上就行;為保證用戶體驗(yàn),及時(shí)通過中間狀態(tài)的形式反饋給用戶,比如常見的出票中、數(shù)據(jù)處理中等。

對(duì)于強(qiáng)一致性和弱一致性的解決方案一般針對(duì)數(shù)據(jù)一致性和時(shí)效性要求特別高的業(yè)務(wù)場(chǎng)景,通常會(huì)犧牲暫時(shí)的可用性來滿足一致性的要求;由于為保證一致性,會(huì)鎖定資源,在高并發(fā)的業(yè)務(wù)場(chǎng)景不是最佳選擇,所以很多系統(tǒng)在業(yè)務(wù)需求允許的情況下,基本上都會(huì)采用最終一致性方案。

正文

1.1 最終一致性簡(jiǎn)述

顧名思義就是保證數(shù)據(jù)最后的一致性就行了。如果中間節(jié)點(diǎn)發(fā)生失敗,系統(tǒng)為了減少代價(jià),一般不會(huì)自動(dòng)回滾,而是通過重試機(jī)制和人工參與的方式對(duì)失敗數(shù)據(jù)進(jìn)行處理,從而保證系統(tǒng)高并發(fā)場(chǎng)景下高可用數(shù)據(jù)一致性需求。

1.2 解決方案

目前用得最多的方案是結(jié)合本地消息表進(jìn)行實(shí)現(xiàn),再加上后臺(tái)任務(wù)、消息隊(duì)列中間件就可以更好的實(shí)現(xiàn)分布式事務(wù)的處理。

解決方案流程

本地消息表:就是在對(duì)應(yīng)業(yè)務(wù)數(shù)據(jù)庫(kù)中增加的一張消息表;這張表存儲(chǔ)業(yè)務(wù)產(chǎn)生的消息,通過本地事務(wù)保證業(yè)務(wù)數(shù)據(jù)和消息數(shù)據(jù)的一致性。在消息表中通過一個(gè)狀態(tài)來標(biāo)識(shí)業(yè)務(wù)是否執(zhí)行成功,如果失敗,后臺(tái)任務(wù)就進(jìn)行重試。

1.2.2 CAP框架簡(jiǎn)介

CAP 是一個(gè)EventBus(事件總線),同時(shí)也是一個(gè)在微服務(wù)或者SOA系統(tǒng)中解決分布式事務(wù)問題的一個(gè)框架,基于CAP理論思想進(jìn)行封裝的。采用模塊化設(shè)計(jì),具有高度的可擴(kuò)展性,可靠并且易于更改。

對(duì)于分布式事務(wù)的處理,CAP 框架采用的是“異步確保”這種方案,即本地消息表。官方支持的數(shù)據(jù)存儲(chǔ)方式有SQL Server、MySQL、PostgreSql、MongoDB、In-Memory(內(nèi)存),由于是開源項(xiàng)目,社區(qū)大佬也提供了其他數(shù)據(jù)存儲(chǔ)支持,如:Oracle、SQLite、SmartSql等。

在分布式系統(tǒng),各節(jié)點(diǎn)需要進(jìn)行消息傳輸,CAP框架提供以下幾種方式RabbitMQ、Kafka、Redis Streams(Redis 5.0支持)、Azure Service Bus、Amazon SQS、In-Memory Queue,使用方式都差不多。

CAP的架構(gòu)圖如下:

架構(gòu)圖

上圖簡(jiǎn)要說明:

  • 有兩個(gè)微服務(wù),服務(wù)A和服務(wù)B;

  • 服務(wù)A中通過本地事務(wù)的方式,將事件消息和業(yè)務(wù)邏輯進(jìn)行事務(wù)保存(事件消息保存在本地消息表中),保證業(yè)務(wù)邏輯和消息的一致性和可靠性;關(guān)于消息的處理和保存CAP已經(jīng)封裝在內(nèi)部;

  • CAP內(nèi)部定時(shí)調(diào)度任務(wù)將消息發(fā)布到消息隊(duì)列中;

  • 服務(wù)B訂閱到消息,將其保存到服務(wù)B的本地消息表中,CAP已經(jīng)封裝好,只需按照說明使用即可;

  • 如果業(yè)務(wù)處理失敗,服務(wù)B中集成的CAP會(huì)根據(jù)配置的定時(shí)任務(wù)策略進(jìn)行重試,直到處理成功為止;

主要的理論就說那么多,更多詳細(xì)內(nèi)容,請(qǐng)進(jìn)下方傳送門:

  • 官網(wǎng):https://cap.dotnetcore.xyz/

  • github:https://github.com/dotnetcore/CAP/blob/master/README.zh-cn.md

接下來就到擼碼時(shí)刻,CAP由于封裝比較好,所以使用起來比較簡(jiǎn)單。

1.3 擼碼實(shí)踐

以下的業(yè)務(wù)場(chǎng)景是為了案例演示,目的是體現(xiàn)CAP的實(shí)踐,所以業(yè)務(wù)邏輯都只是模擬,切勿當(dāng)真。

1.3.1 環(huán)境準(zhǔn)備

演示中要用到RabbitMQ,為了安裝方便,這里使用Docker的方式,直接通過鏡像運(yùn)行,簡(jiǎn)單,快速方便。關(guān)于Docker的實(shí)踐,后續(xù)會(huì)專門出系列文章。這里就先總結(jié)一下Docker的安裝和RabbitMQ在Docker中的運(yùn)行步驟,采用的主機(jī)環(huán)境是我之前買的阿里云服務(wù)器(CentOS 7);演示用的數(shù)據(jù)庫(kù)是SqlServer。

  • Docker安裝

    1、移除移動(dòng)舊版本

    sudo?yum?remove?docker?\docker-client?\docker-client-latest?\docker-common?\docker-latest?\docker-latest-logrotate?\docker-logrotate?\docker-engine

    2、安裝需要的依賴包

    sudo?yum?install?-y?yum-utils

    3、設(shè)置鏡像倉(cāng)庫(kù)

    sudo?yum-config-manager?\--add-repo?\https://download.docker.com/linux/centos/docker-ce.repo

    4、更新Yum軟件包索引

    sudo?yum?makecache?fast?#?提高安裝速度

    5、開始安裝Docker

    sudo?yum?install?docker-ce?docker-ce-cli?containerd.io

    6、啟動(dòng)Docker

    sudo?systemctl?start?docker

    7、測(cè)試Docker

    sudo?docker?run?hello-world?#?運(yùn)行Hello-world 安裝成功
  • RabbitMQ在Docker中安裝和運(yùn)行

    1、一行命令直接指定鏡像運(yùn)行,如果本地找不到鏡像,會(huì)去遠(yuǎn)程倉(cāng)儲(chǔ)里去找。

    docker?run?-d?--hostname?my-rabbit?--name?cap-rabbit?-p?8888:15672?-p?5672:5672?-p?5671:5671?-p?1883:1883?rabbitmq:3-management

    這里先不細(xì)說命令了,后續(xù)聊Docker的時(shí)候好好說說。命令需要注意的是主機(jī)端口和容器端口的映射。

    2、運(yùn)行成功后就可以訪問啦,默認(rèn)用戶名和密碼:guest/guest;

    這里訪問的地址端口是8888,那是在啟動(dòng)容器的時(shí)候?qū)⒅鳈C(jī)端口8888和容器端口15672進(jìn)行了映射。

這就是選擇Dokcer安裝的原因,超級(jí)快;如果用傳統(tǒng)的方式,還得安裝語(yǔ)言環(huán)境,還得配置,最后才能安裝;Docker通過鏡像的方式直接運(yùn)行即可。

如果小伙伴新增用戶之后不能訪問,或者程序連接報(bào)錯(cuò),可以排查是否有權(quán)限訪問,如下:

注:如果小伙伴用的是云服務(wù)器,需要配置安全組,允許端口訪問;另外如果程序和RabbitMq所在的主機(jī)不是同一臺(tái)機(jī)器,主機(jī)防火墻也需要放開對(duì)應(yīng)的端口。

1.3.2 開始擼碼
  • 項(xiàng)目準(zhǔn)備

    這里模擬兩個(gè)服務(wù),一個(gè)是訂單服務(wù),一個(gè)是庫(kù)存服務(wù),兩都用到EF(Code First),如果小伙伴對(duì)EF入門還不熟,<<跟我一起學(xué).NetCore之EF Core 實(shí)戰(zhàn)入門,一看就會(huì)>>這篇文章超詳細(xì),肯定能幫到你;所以接下來就上幾張關(guān)鍵的圖就行啦。

    項(xiàng)目結(jié)構(gòu):

    OrderDbContext:

    Startup中注冊(cè)服務(wù):

    庫(kù)存服務(wù)的代碼和這個(gè)類似。

    通過遷移并更新到數(shù)據(jù)庫(kù)時(shí),會(huì)生成如下數(shù)據(jù)庫(kù)和表:

  • 集成CAP

    這里因?yàn)橛玫氖荝abbitMQ、SqlServer,所以需要引入以下幾個(gè)包;如果用其他消息隊(duì)列或數(shù)據(jù)庫(kù),可以引入對(duì)應(yīng)的包。

    因?yàn)?strong>訂單服務(wù)是在Respository層使用CAP,所以對(duì)應(yīng)的包就在這層引用;

    庫(kù)存服務(wù)是直接在Controller那層引用,這里就不重復(fù)截圖啦。

    訂單服務(wù)和庫(kù)存服務(wù)都是在各自項(xiàng)目的Startup文件中注冊(cè)CAP相關(guān)服務(wù),并配置相關(guān)信息,如下圖:

    集成完畢之后,啟動(dòng)項(xiàng)目(不需要手動(dòng)自己遷移),在各自業(yè)務(wù)數(shù)據(jù)庫(kù)中就自動(dòng)生成兩個(gè)消息表,用于后續(xù)消息的存儲(chǔ),如下:

  • 編寫業(yè)務(wù)代碼

    訂單服務(wù),在訂單生成成功之后,向庫(kù)存服務(wù)發(fā)送消息,業(yè)務(wù)邏輯如下:

    圖中用到的_capPublisher是通過構(gòu)造函數(shù)注入的。訂單服務(wù)其他層的代碼就不用截圖了,就是簡(jiǎn)單調(diào)用,源碼地址在文末。

    庫(kù)存服務(wù)直接訂閱就行,演示案例中是直接在StockController中進(jìn)行訂閱,如下:

    //?標(biāo)記為不實(shí)Action [NonAction] //?訂閱消息,參數(shù)和發(fā)布時(shí)指定的參數(shù)一致 [CapSubscribe("Order.Create.Success")] public?void?UpdateStock(OrderEntity?order) {//throw?new?Exception("扣減庫(kù)存異常了~~~");//?為了測(cè)試,庫(kù)存里面沒有數(shù)據(jù)的話,先模擬一條數(shù)據(jù)bool?bHaveData?=?_stockDbContext.Stock.Any();if(!bHaveData){StockEntity?stock?=?new?StockEntity{Id?=?Guid.NewGuid(),ProductNo?=?"Product001",StockCount?=?100,UpdateDate?=?DateTime.Now};_stockDbContext.Stock.Add(stock);_stockDbContext.SaveChanges();}//?模擬扣減庫(kù)存using?var?trans?=?_stockDbContext.Database.BeginTransaction(_capPublisher,?autoCommit:?false);try{//?根據(jù)產(chǎn)品編號(hào)找到產(chǎn)品var?product?=?_stockDbContext.Stock.Where(s?=>?s.ProductNo?==?order.ProductNo).FirstOrDefault();//?扣減庫(kù)存之后保存product.StockCount?=?product.StockCount?-?order.Count;_stockDbContext.Update(product);_stockDbContext.SaveChanges();//?可以繼續(xù)向下發(fā)布流程,比如庫(kù)存扣減成功,下一步到物流服務(wù)進(jìn)行相關(guān)處理,可以繼續(xù)發(fā)布消息//?_capPublisher.Publish();trans.Commit();Console.WriteLine(order.OrderNo);}catch?(Exception?ex){trans.Rollback();} }

    可以看到,訂閱很簡(jiǎn)單,直接標(biāo)上[CapSubscribe("Order.Create.Success")]這個(gè)Attribute就行了,如果消息狀態(tài)為失敗,后續(xù)CAP的定時(shí)任務(wù)會(huì)根據(jù)定時(shí)策略調(diào)用此方法。

1.3.3 運(yùn)行看效果
  • 正常流程,下單成功,扣減庫(kù)存成功

    將訂單服務(wù)(端口5000)和庫(kù)存服務(wù)(端口6000)都啟動(dòng)起來。

    訂單服務(wù)中增加了OrderController,里面有一個(gè)GenerateOrder的接口,直接調(diào)用即可:

    這里使用Postman工具進(jìn)行測(cè)試,如下:

    庫(kù)存服務(wù)就會(huì)訂閱到信息,如下:

    業(yè)務(wù)流程完成之后,訂單和庫(kù)存數(shù)據(jù)整體一致了,回過頭來看看消息表,看看里面有什么消息,如下:

  • 異常流程模擬,下單成功,扣減庫(kù)存失敗

    在扣減服務(wù)邏輯方法中手動(dòng)拋出異常,代碼如下:

    然后啟動(dòng)項(xiàng)目重新測(cè)試,再下一個(gè)訂單試試;操作后,先來看看消息表,如下:

    注:CAP在默認(rèn)情況下,發(fā)送和消費(fèi)消息的過程中失敗會(huì)立即重試 3 次,在 3 次以后將進(jìn)入重試輪詢;重試將在發(fā)送和消費(fèi)消息失敗的 4分鐘后 開始,這是為了避免設(shè)置消息狀態(tài)延遲導(dǎo)致可能出現(xiàn)的問題;后續(xù)就會(huì)每隔1分鐘之后重試一次,默認(rèn)的最高重試次數(shù)為50次,當(dāng)達(dá)到50次時(shí),就不會(huì)重試了。

    現(xiàn)在知道問題了,優(yōu)化代碼,重新啟動(dòng),即把拋異常的代碼注釋掉,看看會(huì)不會(huì)自動(dòng)處理,如下:

    如上圖,稍等一會(huì),消息就自動(dòng)處理了,業(yè)務(wù)數(shù)據(jù)符合預(yù)期,保證一致性。這個(gè)是CAP內(nèi)部定時(shí)讀取消息表,根據(jù)狀態(tài)不斷重試業(yè)務(wù)邏輯,直到成功為止。CAP的全自動(dòng)是不是感覺比較便捷,寫最少的代碼,解決了最難搞的分布式事務(wù)。

  • 修改默認(rèn)的配置

    在實(shí)際業(yè)務(wù)場(chǎng)景中,默認(rèn)配置可能不太實(shí)用,可以在注冊(cè)服務(wù)時(shí)進(jìn)行默認(rèn)配置更改,如下:

    配置修改之后的測(cè)試這里就不截圖了,留給小伙伴們動(dòng)手試試吧。

案例代碼地址:https://gitee.com/CodeZoe/microservies-demo/tree/main/CapDemo

總結(jié)

關(guān)于分布式事務(wù)的實(shí)操,把最常用的最終一致性方案簡(jiǎn)單分享了一下,小伙伴可以根據(jù)自己的業(yè)務(wù)場(chǎng)景,趕緊動(dòng)手試試吧;

其他方案會(huì)在后續(xù)的文章中加上,主要還是以實(shí)用為主,已經(jīng)不咋用的就沒必要再說啦。

文章中提及到Docker和RabbitMQ,我已經(jīng)在著手準(zhǔn)備這塊的文章了,關(guān)注“Code綜藝圈”,和我一起學(xué)習(xí)吧;

總結(jié)

以上是生活随笔為你收集整理的分布式事务最终一致性-CAP框架轻松搞定的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。