業(yè)務(wù)層面冪等
冗余部署多個進程
存在并發(fā)消費的可能性
并發(fā)轉(zhuǎn)變成串行消費
發(fā)送端會發(fā)送很多次扁位,消費端可能會消費很多次,需要做去重,對共享資源做去重,其實也是一個分布式鎖的問題
本質(zhì)
分布式鎖問題
分布式鎖設(shè)計與實踐
分布式鎖的定義
分布式環(huán)境下齿桃,鎖定全局唯一資源
請求處理串行化
實際表現(xiàn)互斥鎖
分布式鎖的目的
交易訂單鎖定
防止重復下單
解決業(yè)務(wù)層冪等問題
MQ消息消費冪等性
發(fā)送消息重復
消息消費端去重
比如手機提現(xiàn)
在用戶對商品下單后,訂單狀態(tài)為待支付煮盼,在某一時刻用戶正在對該訂單做支付操作短纵,商家對該訂單進行改價操作
狀態(tài)的修改行為需要做串行處理,避免出現(xiàn)數(shù)據(jù)錯亂
基于Redis 分布式鎖
基于Redis 分布式鎖方案
1僵控、唯一線程串行處理
2香到、實現(xiàn)方式
2.1 Redis Setnx(SET if Not eXists) 命令在指定的key不存在時,為key 設(shè)置指定的值
SETNX KEY_NAME VALUE Expire_Time
設(shè)置成功报破,返回1 設(shè)置失敗 返回0
如果Redis 是單機的話很危險悠就,比如T1 在redis上加了一把鎖,這時候Redis掛了泛烙,然后重啟理卑,這時候T2也對相同資源加了一把鎖翘紊,此時就有兩把鎖蔽氨, 當然你對鎖做持久化,但是從架構(gòu)層面上來講是沒有必要的,因為事務(wù)的生命周期比較短鹉究。
2.2 存在問題
鎖時間不可控
無法續(xù)租期
redis鎖依賴于失效時間 expire宇立,比如失效時間為5秒鐘,但是5秒鐘我的事務(wù)還沒執(zhí)行完自赔,鎖失效了妈嘹,另外一個線程又拿到了這把鎖,又得重復去做這個事情绍妨。
單點問題
單實例存在進程一旦死掉润脸,會徹底阻塞業(yè)務(wù)流程
主從方式,主從數(shù)據(jù)異步他去,會存在鎖失效問題
2.2.1毙驯、第一種情況單機如果掛掉的話,如果你的服務(wù)強依賴redis灾测,redis 還沒起來的話爆价,會阻塞業(yè)務(wù)流程。
2.2.2媳搪、第二情況 T1寫到主redis,如果redis 主掛了铭段,還沒有同步到從redis ,根據(jù)哨兵原理秦爆,從redis會切換為主序愚,T2會從新的主去拿,此時T2也會重新生成一把鎖等限,這種情況下展运,T1、T2都會拿到這把鎖精刷。這樣就會出現(xiàn)重復鎖的問題
那么基于Redis分布式鎖到底能不能使用拗胜?取決于你的業(yè)務(wù)場景決定的。
如果你的業(yè)務(wù)是交易場景怒允,嚴格要求他的一致性埂软,極端情況下,鎖重復問題一定是不能忍的纫事。
如果是社交發(fā)消息場景勘畔,極端情況下,如果鎖重復的話丽惶,發(fā)消息重新消費一次炫七,也是能接受的。
redis 作為鎖
高可用無法保證
問題本質(zhì)钾唬,分布式鎖是CP模型万哪,Redis集群是AP模型侠驯,Redis是半同步的。 所以要通過CP模型解決 Redis 自己實現(xiàn)了一套RAFT協(xié)議:redlock
3奕巍、官方建議
Redis 本身建議使用Redlock 算法來保證吟策,但是問題是需要至少三個Redis主從實例來完成,維護成本相對比較高的止,Redlock等同于自己實現(xiàn)簡單的一致性協(xié)議檩坚,細節(jié)繁瑣,且容易出錯诅福。
高可用分布式鎖設(shè)計目標
設(shè)計目標
1匾委、強一致性
其實是任何鎖都要做的事情
2、服務(wù)高可用氓润、系統(tǒng)穩(wěn)健
要保證服務(wù)的高可用剩檀,不能因為一臺機器宕機了就不可用了
3、鎖自動續(xù)約及其自動釋放
要有能力旺芽,在業(yè)務(wù)無感知的情況下沪猴,做自動的續(xù)約,比如Redis 鎖過期時間是5秒鐘采章,當我的事務(wù)5秒鐘還沒有結(jié)束的時候运嗜,自動再續(xù)約5秒鐘。
4悯舟、代碼高度抽象業(yè)務(wù)接入極簡
當然也希望我的代碼比較抽象担租,接入也比較簡單,
5抵怎、可視化管理后臺奋救,監(jiān)控及管理
高可用分布式鎖設(shè)計方案對比
存儲層產(chǎn)品對比
redis | zookeeper | etcd | |
---|---|---|---|
一致性算法 | 無 | paxos(zab) | raft |
CAP | AP | CP | CP/AP |
高可用 | 主從 | N+1可用 | N+1可用 |
接口類型 | 客戶端 | 客戶端 | http/grpc |
實現(xiàn) | setNX | createEphemeral | restful API |
zookeeper 為什么是n+1 是因為是奇數(shù)個,etcd 客戶端比較糟糕
1反惕、由于Redis 無法保證數(shù)據(jù)一致性
2尝艘、Zookeeper對鎖實現(xiàn)使用創(chuàng)建臨時節(jié)點和watch機制,執(zhí)行效率姿染、擴展能力背亥、社區(qū)活躍度等方面低于etcd
3、選擇基于etcd 實現(xiàn) tidb也是基于 etcd來實現(xiàn)的
分布式鎖存儲選型
etcd
簡單KV
強一致
高可用--無單點
數(shù)據(jù)高可靠--持久化
分布式鎖整體方案
分布式Client+etcd
clientTTL模式
Client TTL模式
1悬赏、ClientA->etcd->("key","ttl","value","uuid")
2狡汉、ClientB->etcd->("key","ttl","value","uuid");
etcd 只需要填 key ttl ,value隨便填,對于鎖來說value無所謂闽颇,uuid不需要填盾戴,當你成功拿到鎖以后,etcd集群會給你生成一個uuid兵多,這個uuid其實就是你的鎖的唯一的憑證尖啡,接下來所以對鎖的操作橄仆,都是基于這個uuid來實現(xiàn)的。
3可婶、ClientA 拿鎖成功沿癞,ClientB 拿鎖失敗
4援雇、A服務(wù)需要對etcd 保持后臺心跳線程矛渴,比如key的租期是10ms,后臺心跳線程為3ms,心跳線程負責在拿到key之后,每3ms cas 唯一憑證uuid;
比如key的租期是10ms,那么我們的心跳往往會設(shè)置租期的1/3的時間惫搏,也就是3ms,每次心跳的就是就會續(xù)約租期具温,當心跳到的時候,租期還有10-3=7ms,這時候我會刷新ttl改成10ms筐赔,相當于又增加了3ms铣猩。
什么時候釋放呢,這個時候需要業(yè)務(wù)方主動去釋放這個鎖
使用場景一:申請鎖
1茴丰、業(yè)務(wù)放申請資源鎖达皿,調(diào)用時提供key,ttl
2、etcd生成uuid贿肩,作為當前鎖的唯一憑證峦椰,將(key,uuid,ttl)寫etcd
3、檢查etcd中此key 是否存在汰规,如沒有汤功,嘗試寫入key,寫入失敗溜哮,拿鎖失敗滔金,寫入成功拿到鎖。
4茂嗓、拿到鎖后餐茵,客戶端異步心跳線程啟動,心跳線程維持時間為ttl/3,compare and swap uuid ,從而將key 值續(xù)租
5述吸、相關(guān)etcd API
a钟病、申請鎖
curl http://127.0.0.12379/v2/keys/foo -XPUT -d value=bar -d ttl=5 prevExist=false;
b、CAS更新鎖租約
curl http://127.0.0.12379/v2/keys/foo?prevValue=prev_uuid -XPUT -d ttl=5 refresh=true prevExist=true;
c刚梭、CAS刪除鎖
curl http://127.0.0.12379/v2/keys/foo?prevValue=prev_uuid -XDELETE;
使用場景二:申請鎖肠阱,但鎖已被持有
1、業(yè)務(wù)方申請資源鎖朴读,調(diào)用時提供key.ttl
2屹徘、檢查etcd中key的存在,若已存在衅金,拿鎖失敗
使用場景三:鎖的清理
1噪伊、如果調(diào)用方正常結(jié)束簿煌,通過cas 接口調(diào)用delete方法自動清理etcd中的key值
2、如果調(diào)用方異常終止鉴吹,等待原有鎖ttl 過期后姨伟,鎖資源釋放。
如果沒有調(diào)用 delete方法豆励,心跳超時以后夺荒,過時間過期后,鎖會自動釋放良蒸。
業(yè)務(wù)接入
JDK 7 及以上
獲取鎖示例
try(zzlock=zzlock.getlock("resource_id",ttl)){
//jobs
zzlock.isTrue();
}
//釋放鎖示例
Optional<LockItem> lockItem=getlock("key");
if(lockItem.isPresent()){
System.out.println("獲得鎖技扼,如果進程被終止,鎖會在10s后失效嫩痰!");
System.out.println("如果進程繼續(xù)剿吻,后臺心跳線程更新3s鎖時間");
releaseLock(lockItem.get());
}else{
System.out.println("獲取鎖的失敗4摹@雎谩!");
}
獲取鎖平均耗時監(jiān)控
1纺棺、下面是獲取鎖的平均耗時情況
a榄笙、獲取鎖的平均耗時大概是在2.1ms左右
b、由于etcd的強一致性五辽,根據(jù)raft算法办斑,消耗時間稍微長一點
etcd兼容性測試
etcd提供獨有的集群管理模式,方便進行極端case下的測試杆逗,以三個節(jié)點的etcd集群為例
a乡翅、單節(jié)點停機,不影響持續(xù)寫入罪郊,不影響讀蠕蚜,結(jié)果有一致性
b、當只有一個節(jié)點時悔橄,讀會停機靶累,寫入正常
c、理論上只要不是多節(jié)點同時停機癣疟,線上服務(wù)不會受影響
etcd 恢復/版本
1挣柬、etcd 有自有的數(shù)據(jù)恢復方式,如果服務(wù)停機后睛挚,可以將所有數(shù)據(jù)轉(zhuǎn)移重啟
2邪蛔、etcd的增刪節(jié)點,節(jié)點遷移等部署相關(guān)扎狱,均有相關(guān)操作方式
3侧到、etcd 版本選擇勃教,選擇使用etcd 3.2.9 ,但是因為V3 API 暫時還不夠完備匠抗,建議用V2 方式實現(xiàn)
a故源、V3提供gRPC接口
b、天然提供分布式鎖功能汞贸,只需申請鎖绳军,釋放鎖,不用關(guān)注鎖的租期問題著蛙。
分布式鎖特殊場景
1删铃、特殊場景一:分布式鎖只是在同一自然時間的互斥鎖耳贬,本身不解決冪等性問題踏堡;接入業(yè)務(wù)需要完善從獲得鎖到釋放鎖中間的數(shù)據(jù)冪等邏輯。
例如:T1 拿到lock 很快就處理完成了咒劲,T2 也拿到lock做同樣的操作顷蟆,也是可以的。所以它不能解決冪等問題腐魂,只是同一自然時間的互斥鎖帐偎。
T1 拿到訂單了進行業(yè)務(wù)處理,T2也拿到了訂單也進行業(yè)務(wù)處理蛔屹,要做冪等只需要判斷訂單的狀態(tài)削樊,比如這個訂單已經(jīng)支付了,很顯然就不能重復支付兔毒。這個冪等只能業(yè)務(wù)方去處理漫贞,分布式鎖不會幫你去解決這個問題。
2育叁、特殊場景二:鎖沒有按照預期續(xù)租
a迅脐、心跳續(xù)租沒成功
b、馬上啟動GC豪嗽,GC時間夠長
缺點1:etcd的租期為10秒鐘谴蔑,這時候每次心跳3秒去續(xù)租,這時候網(wǎng)絡(luò)異常龟梦,有可能續(xù)租沒有成功隐锭。
缺點2:etcd 租期為10秒,這時候服務(wù)器執(zhí)行FullGC计贰,這個FullGC 是11秒鐘钦睡,當然GC 要11秒鐘,你的服務(wù)器可能要優(yōu)化蹦玫,每3秒的時候就在執(zhí)行續(xù)租的時候赎婚,GC導致業(yè)務(wù)方的請求暫停了刘绣,當你停止以后已經(jīng)11秒過后了,這時候鎖已經(jīng)被釋放掉了挣输。
3纬凤、特殊場景三:etcd內(nèi)部協(xié)調(diào)發(fā)生問題
a、Leader節(jié)點掛了撩嚼,選主中
選主這個過程中停士,是停止響應(yīng)的,是沒辦法拿到鎖的
b完丽、Raft日志數(shù)據(jù)同步發(fā)生錯誤或者不一致問題
當Rfat 發(fā)生同步錯誤恋技,因為它是CP模型,這時候不會讓你寫或者讀逻族。
consul 也是cp模型 consul 生態(tài)沒有etcd 龐大
為什么k8s 選中etcd 作為它的整個的注冊中心蜻底?
k8s 沒有好的選擇,k8s 當你的集群特別大的時候聘鳞,當你的日志就是你的事件需要寫的時候薄辅,etcd 就會成為你的瓶頸,這時候要怎么辦抠璃,要定期清理你的etcd事件的同步站楚。
冪等需要兩個層次,一是不并發(fā)搏嗡;二是重復消費結(jié)果一樣窿春;分布式鎖的作用就是“串行”;
Redission里實現(xiàn)的分布式鎖初始過期時間也是30秒
一臺機器 耗時 2.1ms qps=500
當業(yè)務(wù)線程假死采盒,ttl線程還在跑旧乞,這個只能ttl超時了自動釋放
線上鎖的最長時間是10秒鐘
zk做分布式鎖,因為是CP模型纽甘,當寫入量很大的話會有問題良蛮?其實對鎖還好,無非就是生成一把鎖會寫一次悍赢,心跳更新租約再寫一次决瞳,delete鎖再寫一次;
如果業(yè)務(wù)阻塞了左权,假死了無法釋放鎖皮胡,此時心跳還在,心跳還在就會自動續(xù)租赏迟。
這時候網(wǎng)關(guān)發(fā)現(xiàn)服務(wù)器假死屡贺,觸發(fā)熔斷,會發(fā)送通知給控制中心,控制中心重啟服務(wù)甩栈,這時候心跳檢測程序就沒有了泻仙;
或者提供一個鎖的最長超時時間,定義最大續(xù)約次數(shù)量没。
舉個例子:有兩個進程拿到相同的orderId,進程1拿到了鎖玉转,先處理了訂單,完事之后更新狀態(tài)殴蹄,進程2拿不到鎖究抓,或通過檢測訂單的狀態(tài),來避免重復執(zhí)行相同的操作袭灯。
數(shù)據(jù)一致性定義
任何人
任何時間
任何地點
任何接入方式
任何服務(wù)
數(shù)據(jù)都是一致
數(shù)據(jù)不一致性產(chǎn)生原因
1刺下、數(shù)據(jù)分散在多處
a、多個DB稽荧;b橘茉、DB和緩存
2、電商交易平臺案例
用戶蛤克、商品捺癞、交易等功能
分布式事務(wù)場景
1夷蚊、電商下單場景
a构挤、下單
b、發(fā)送消息到MQ
2惕鼓、一致性保證
a筋现、本地事務(wù)
下單操作
發(fā)送MQ消息操作
放進一個本地事務(wù)
上述做法有什么問題?
java 實例代碼
try{
stmt=DriverManager.getConnection("jdbc:mysqlxxxx");
stmt=conn.createStatement();
//數(shù)據(jù)庫生成訂單操作
stmt.executeUpdate("insert order values(orderId,timestamp,price,state)");
//生成發(fā)送的消息內(nèi)容
MsgObject MsgContent(orderID);
//發(fā)送消息操作
MQClient.sendMsg(MsgContent);
//事務(wù)提交
conn.commit();
}catch(Exception e){
e.printStackTrace();
try{
//操作不成功則返回退
conn.rollback();
}catch(Exception ex){
ex.printStackTrace();
}
}
分布式事務(wù)分類
1箱歧、剛性分布式事務(wù)
a矾飞、強一致性
b、XA 模型
XA模型是完全遵循ACID的高可用事務(wù)
c呀邢、CAP:CP
2洒沦、柔性分布式事務(wù)
a、最終一致性
b价淌、CAP申眼、BASE理論 AP
剛性分布式事務(wù)
1、滿足傳統(tǒng)事務(wù)特性
ACID(Atomicity-原子性蝉衣、Consistency-一致性括尸、Isolation-隔離性、Durability-持久性)
2病毡、XA模型
a濒翻、XA是X/Open CAE Specification(Distributed Transaction Processing )模型定義,XA規(guī)范由AP、RM 有送、TM組成淌喻。
b、其中應(yīng)用程序(Application Program,簡稱AP):AP定義事務(wù)邊界(定義事務(wù)開始和結(jié)束)并訪問事務(wù)邊界內(nèi)的資源雀摘。
c似嗤、資源管理器(Resource Manager,簡稱RM):RM 管理計算機共享的資源,資源即數(shù)據(jù)庫等届宠。
d烁落、事務(wù)管理器(Transaction Manager ,簡稱TM):負責管理全局事務(wù),分配事務(wù)唯一標識豌注,監(jiān)控事務(wù)的執(zhí)行進度伤塌,并負責事務(wù)的提交、會滾轧铁、失敗恢復等每聪。
剛性分布式事務(wù)
1、案例:組織爬山
2齿风、過程:
a药薯、二階段提交,是XA規(guī)范標準實現(xiàn)
b救斑、TM 發(fā)起prepare投票
c童本、RM 都同意后,TM再發(fā)起commit
d脸候、Commit過程出現(xiàn)宕機等異常穷娱,節(jié)點服務(wù)重啟后,根據(jù)XA recover再次進行commit補償
3运沦、缺點
a泵额、同步阻塞模型
b、數(shù)據(jù)庫資源鎖定時間過長
它的鎖庫時間很長
現(xiàn)在是兩個庫携添,所以同時要鎖定兩個庫嫁盲;
如果是100個庫,那么就要同時鎖定100個庫烈掠;
c羞秤、全局鎖(隔離級別串行化),并發(fā)低向叉。
基于XA的分布式事務(wù)如果要嚴格保證ACID锥腻,實際需要事務(wù)隔離級別為SERLALIZABLE。這時候會鎖表母谎,這個隔離級別是非常粗的瘦黑,所以并發(fā)也低。
d、不適合長事務(wù)場景
長事務(wù)就是一個事務(wù)里面操作很多步驟幸斥,比如大于3匹摇;所以2PC 比較適合短事務(wù)。
柔性分布式事務(wù)
1甲葬、CAP
分布式環(huán)境下P一定需要廊勃,CA權(quán)衡折中
2、BASE理論
a经窖、Basically Available 基本可用
b坡垫、Soft state-柔性狀態(tài)
c、Eventual consistency 最終一致性
3画侣、架構(gòu)思考
柔性事務(wù)是對XA協(xié)議的妥協(xié)冰悠,它通過降低強一致性要求,從而降低數(shù)據(jù)庫資源鎖定時間配乱,提升可用性
4溉卓、典型架構(gòu)實現(xiàn)
a、TCC模型
b搬泥、Saga模型
TCC模型
1桑寨、Try-Confirm-Cancel
2、TCC模型完全交由業(yè)務(wù)實現(xiàn)忿檩,每個子業(yè)務(wù)都需要實現(xiàn)Try-Confirm-Cancel接口尉尾,對業(yè)務(wù)侵入大
假如:用戶購買流程:下單->減庫存->支付,那么你每一個步驟都要實現(xiàn)一遍Try-Confirm-Cancel
對業(yè)務(wù)侵入很大休溶,在業(yè)務(wù)層實現(xiàn)
a代赁、資源鎖定交由業(yè)務(wù)方
3、Try
嘗試執(zhí)行業(yè)務(wù)兽掰,完成所有業(yè)務(wù)檢查,預留必要的業(yè)務(wù)資源
每次提交之前徒役,都要去判斷是否具備提交的必要條件
4孽尽、Confirm
真正執(zhí)行業(yè)務(wù),不再做業(yè)務(wù)檢查
5忧勿、Cancel
釋放Try階段預留的業(yè)務(wù)資源
6褂策、匯款服務(wù)由捎、收款服務(wù)案例
A用戶向B用戶匯款500元
匯款服務(wù):
a.Try
1、檢查A賬戶有效性,即查看A賬戶的狀態(tài)是否為“轉(zhuǎn)賬中”或者“凍結(jié)”像棘;
2、檢查A賬戶余額是否充足攀甚;
3担忧、從A賬戶中扣減500元,并將狀態(tài)置為“轉(zhuǎn)賬中”;
4哼勇、預留扣減資源都伪,將從A往B賬戶轉(zhuǎn)賬500元這個事件存入消息或者日志中
b.Confirm:
1、不做任何操作
c.Cancel:
1积担、A賬戶增加500元陨晶;
2、從日志或者消息中帝璧,釋放扣減資源先誉;
收款服務(wù)
a.Try:
1.檢查B賬戶賬戶是否有效
b.Confirm:
1.讀取日志或者消息,B賬戶增加500元的烁;
2.從日志或者消息中谆膳,釋放扣減資源;
c.Cancel:
1.不做任何操作撮躁;
柔性分布式事務(wù)--Saga模型
1漱病、起源于1987年Hector & Kenneth 發(fā)表的論文Sagas
2、Saga 模型把一個分布式事務(wù)拆分為多個本地事務(wù)把曼,每個本地事務(wù)都有相應(yīng)的執(zhí)行模塊和補償模塊(對應(yīng)TCC的Confirm和Cancel)
2杨帽、當Saga 事務(wù)中任意一個本地事務(wù)出錯時,可以通過調(diào)用相關(guān)的補償方法恢復之前的事務(wù)嗤军,達到事務(wù)最終一致性注盈。
3、當每個Saga 子事務(wù)T1叙赚、T2老客、...Tn 都有對應(yīng)的補償定義C1、C2震叮,....Cn-1,那么Saga系統(tǒng)可以保證
a.子事務(wù)序列T1,T2,......,Tn得完成(最佳情況)
b.或者序列T1,T2,....Tj,Cj-1,.....C2,C1, 0<j<n得以完成
逆序串行執(zhí)行胧砰,保證有序
Saga隔離性
1、業(yè)務(wù)層控制并發(fā)
a.在應(yīng)用層加鎖
b.應(yīng)用層預先凍結(jié)資源等
Saga恢復方式
1苇瓣、向后恢復:補償所有已經(jīng)完成的事務(wù)尉间,如果任一子事務(wù)失敗击罪;
2哲嘲、向前恢復:重試失敗的事務(wù),假設(shè)每個子事務(wù)最終都會成功媳禁;
剛性分布式事務(wù)VS柔性分布式事務(wù)
剛性事務(wù)(XA) | 柔性事務(wù) | |
---|---|---|
業(yè)務(wù)改造 | 無 | 有(需要改造) |
回滾 | 支持 | 實現(xiàn)補償接口 |
一致性 | 強一致 | 最終一致 |
隔離性 | 原生支持 | 實現(xiàn)資源鎖定接口 |
并發(fā)性能 | 嚴重衰退 | 略微衰退 |
適合場景 | 短事務(wù)眠副,并發(fā)較低 | 長事務(wù),高并發(fā) |
解決思路
問題通用解決思路
1竣稽、解決這個問題本身
2囱怕、讓問題本身消失霍弹,圓珠筆筆芯漏油解決
方案一:從業(yè)務(wù)場景消除分布式事務(wù)
1、思路:核心業(yè)務(wù)先處理光涂,其他業(yè)務(wù)異步處理
方案二:柔性分布式事務(wù)
柔性分布式事務(wù)
通用處理思路
1庞萍、本地事務(wù)->短事務(wù)
2、分布式事務(wù)->長事務(wù)
3忘闻、轉(zhuǎn)變成多個短事務(wù)
4钝计、案例
A[下單]->B[減庫存]->C[支付]
A->DB1
B->DB2
C->DB3
A/B/C都成功
A/B 成功,C失敗 補償
業(yè)務(wù)場景
1齐佳、異步場景
基于MQ驅(qū)動分布事務(wù)
2私恬、同步場景
基于異步補償分布
異步場景分布式事務(wù)設(shè)計
異步場景
商品交易
下單、支付
方案一:業(yè)務(wù)方提供本地操作成功回查功能
1炼吴、事務(wù)消息:MQ提供類似X/Open XA 的分布式事務(wù)功能本鸣,通過MQ事務(wù)消息能達到分布式事務(wù)的最終一致
2、半消息:暫不能投遞的消息硅蹦,發(fā)送方已經(jīng)將消息成功發(fā)送到了MQ服務(wù)端荣德,但是服務(wù)端未收到生產(chǎn)者對該消息的二次確認,此時該消息被標記成“暫不投遞”狀態(tài)童芹,處于該種狀態(tài)下的消息即半消息
3涮瞻、消息回查:由于網(wǎng)絡(luò)閃斷、生產(chǎn)者應(yīng)用重啟等原因假褪,導致某條事務(wù)消息二次確認丟失署咽,MQ服務(wù)端通過掃描發(fā)現(xiàn)某條消息長期處于“半消息”時,需要主動向消息生產(chǎn)者詢問該消息的最終狀態(tài)(Coommit或者是Rollback)生音,即消息回查
MQ分布式事務(wù)設(shè)計方案圖:
異步場景分布式事務(wù)設(shè)計
方案一:業(yè)務(wù)方提供本地操作成功回查功能
MQ分布式事務(wù)消息設(shè)計
a宁否、MQ事務(wù)消息設(shè)計事務(wù)消息作為一種異步確保型事務(wù),將兩個事務(wù)分支通過MQ進行異步解耦缀遍,MQ事務(wù)消息的設(shè)計流程同樣借鑒了兩階段提交理論慕匠,整體交互流程如下圖所示:
1、事務(wù)發(fā)起方首先發(fā)送prepare消息到MQ瑟由;
2絮重、在發(fā)送prepare消息成功后執(zhí)行本地事務(wù);
3歹苦、根據(jù)本地事務(wù)執(zhí)行結(jié)果返回commit或者是rollback;
4、如果消息是rollback,MQ將刪除該prepare消息不進行下發(fā)督怜,如果是commit消息殴瘦,MQ將會消息發(fā)送給consumer端;
5号杠、如果執(zhí)行本地事務(wù)過程中蚪腋,執(zhí)行端掛掉丰歌,或者超時,MQ服務(wù)器端將不停的詢問producer來獲取事務(wù)狀態(tài)屉凯;
6立帖、Consumer端的消費成功機制有MQ保證;
該方案的優(yōu)缺點:
優(yōu)點:通用
缺點:
1悠砚、業(yè)務(wù)方需提供回查接口晓勇,對業(yè)務(wù)侵入大;
2灌旧、發(fā)送消息非冪等绑咱;
3、消費端需要處理冪等枢泰;
消息不冪等的意思就是:
1描融、發(fā)送Half消息到MQ Server 成功;
2衡蚂、第二步的時候Half 消息 ack MQ 發(fā)送方失斄恕! 此時生產(chǎn)者會重復第一步毛甲,再次發(fā)送Half消息到MQ Server;
3年叮、發(fā)送多次就會處理多次,所以存在消息不冪等的問題丽啡。
方案二
本地操作和發(fā)送消息通過本地事務(wù)強一致性
1谋右、本地事務(wù)操作表
2、本地事務(wù)消息表
mqMessages(msgid,content,topic,status);
操作表和消息表放在同一個庫补箍,所以是Local Transaction 事務(wù)
比如:下訂單流程改执,我們將下訂單和下訂單產(chǎn)生的消息放到同一個本地事務(wù)里面執(zhí)行。
方案二優(yōu)缺點:
1坑雅、發(fā)送端消息不冪等
At least once (至少一次,肯定都是這個辈挂,下面兩個不要用)
Only once(只有一次)
At more once (最多一次)
2、消費端處理消息冪等
a裹粤、分布式鎖
3终蒂、A->B->C
3.1、A/B成功遥诉,C失斈雌;
比如: A->DB1 并寫MQ矮锈;然后 MQ->B->DB2; MQ->C->DB3;
1霉翔、如果很幸運,B成功了苞笨,C也成功了债朵,事務(wù)就成功了子眶。
2、如果B成功了序芦,C失敗了臭杰,這時候C發(fā)一條會滾消息MQ;
3谚中、然后這個補償消息MQ要被A和B消費渴杆,這時候又回到了1的步驟。這樣就會出現(xiàn)死循環(huán)藏杖。如下圖所示
a将塑、記錄錯誤日志
b、報警
c蝌麸、人工介入
4点寥、優(yōu)點:
業(yè)務(wù)侵入小来吩;
其實現(xiàn)在大部分走的是這種模式敢辩。
實際異步場景使用較多的是方案二:本地事務(wù)消息表
基于半消息實現(xiàn)的“方案一”使用的比較少,現(xiàn)在開源的RockectMQ就是半消息的實現(xiàn)方案弟疆。
會滾MQ需要串行嗎戚长?
如果MQ的處理結(jié)果要告訴別人就要串行,否則無需串行怠苔,可以同時處理多個MQ會滾同廉。
轉(zhuǎn)錢有凍結(jié)狀態(tài),所以不一定最后一步做轉(zhuǎn)錢動作柑司,轉(zhuǎn)錢凍結(jié)狀態(tài)是無法提現(xiàn)的迫肖。
如果用戶將平臺的庫存占住而不支付,怎么處理攒驰?
不支付是有時間限制的蟆湖,比如30分鐘,或者2個小時玻粪,這時候可以發(fā)送一個延遲消息隅津,如果不支付就會被清空掉。
或者定時作業(yè)劲室,定時掃描未支付的訂單伦仍。
對于分布式事務(wù),要盡可能得減少網(wǎng)絡(luò)交互很洋,網(wǎng)絡(luò)交互越多呢铆,發(fā)生事務(wù)處理失敗或者事務(wù)會滾的概率越大。