激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 詳解Java分布式事務的 6 種解決方案

詳解Java分布式事務的 6 種解決方案

2021-09-16 10:55Java技術棧 Java教程

在分布式系統、微服務架構大行其道的今天,服務間互相調用出現失敗已經成為常態,本文側重于其他幾項,關于 2PC、3PC 傳統事務,網上資料已經非常多了,這里不多做重復,本文通過示例給大家介紹Java分布式事務的 6 種解決方

介紹

分布式系統、微服務架構大行其道的今天,服務間互相調用出現失敗已經成為常態。如何處理異常,如何保證數據一致性,成為微服務設計過程中,繞不開的一個難題。 在不同的業務場景下,解決方案會有所差異,常見的方式有:

  1. 阻塞式重試;
  2. 2PC、3PC 傳統事務
  3. 使用隊列,后臺異步處理;
  4. TCC 補償事務;
  5. 本地消息表(異步確保);
  6. MQ 事務。

本文側重于其他幾項,關于 2PC、3PC 傳統事務,網上資料已經非常多了,這里不多做重復。

阻塞式重試

在微服務架構中,阻塞式重試是比較常見的一種方式。偽代碼示例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
m := db.Insert(sql)
 
err := request(B-Service,m)
 
func request(url string,body interface{}){
  for i:=0; i<3; i ++ {
    result, err = request.POST(url,body)
    if err == nil {
        break
    }else {
      log.Print()
    }
  }
}

如上,當請求 B 服務的 API 失敗后,發起最多三次重試。如果三次還是失敗,就打印日志,繼續執行下或向上層拋出錯誤。這種方式會帶來以下問題

  1. 調用 B 服務成功,但由于網絡超時原因,當前服務認為其失敗了,繼續重試,這樣 B 服務會產生 2 條一樣的數據。
  2. 調用 B 服務失敗,由于 B 服務不可用,重試 3 次依然失敗,當前服務在前面代碼中插入到 DB 的一條記錄,就變成了臟數據。
  3. 重試會增加上游對本次調用的延遲,如果下游負載較大,重試會放大下游服務的壓力。

第一個問題:通過讓 B 服務的 API 支持冪等性來解決。

第二個問題:可以通過后臺定時腳步去修正數據,但這并不是一個很好的辦法。

第三個問題:這是通過阻塞式重試提高一致性、可用性,必不可少的犧牲。

阻塞式重試適用于業務對一致性要求不敏感的場景下。如果對數據一致性有要求的話,就必須要引入額外的機制來解決。

異步隊列

在解決方案演化的過程中,引入隊列是個比較常見也較好的方式。如下示例:

?
1
2
3
m := db.Insert(sql)
 
err := mq.Publish("B-Service-topic",m)

在當前服務將數據寫入 DB 后,推送一條消息給 MQ,由獨立的服務去消費 MQ 處理業務邏輯。和阻塞式重試相比,雖然 MQ 在穩定性上遠高于普通的業務服務,但在推送消息到 MQ 中的調用,還是會有失敗的可能性,比如網絡問題、當前服務宕機等。這樣還是會遇到阻塞式重試相同的問題,即 DB 寫入成功了,但推送失敗了。

理論上來講,分布式系統下,涉及多個服務調用的代碼都存在這樣的情況,在長期運行中,調用失敗的情況一定會出現。這也是分布式系統設計的難點之一。

TCC 補償事務

在對事務有要求,且不方便解耦的情況下,TCC 補償式事務是個較好的選擇。

TCC 把調用每個服務都分成 2 個階段、 3 個操作:

  • 階段一、Try 操作:對業務資源做檢測、資源預留,比如對庫存的檢查、預扣。
  • 階段二、Confirm 操作:提交確認 Try 操作的資源預留。比如把庫存預扣更新為扣除。
  • 階段二、Cancel 操作:Try 操作失敗后,釋放其預扣的資源。比如把庫存預扣的加回去。

TCC 要求每個服務都實現上面 3 個操作的 API,服務接入 TCC 事務前一次調用就完成的操作,現在需要分 2 階段完成、三次操作來完成。

比如一個商城應用需要調用 A 庫存服務、B 金額服務、C 積分服務,如下偽代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
m := db.Insert(sql)
aResult, aErr := A.Try(m)
bResult, bErr := B.Try(m)
cResult, cErr := C.Try(m)
if cErr != nil {
    A.Cancel()
    B.Cancel()
    C.Cancel()
} else {
    A.Confirm()
    B.Confirm()
    C.Confirm()
}

代碼中分別調用 A、B、C 服務 API 檢查并保留資源,都返回成功了再提交確認(Confirm)操作;如果 C 服務 Try 操作失敗后,則分別調用 A、B、C 的 Cancel API 釋放其保留的資源。

TCC 在業務上解決了分布式系統下,跨多個服務、跨多個數據庫的數據一致性問題。但 TCC 方式依然存在一些問題,實際使用中需要注意,包括上面章節提到的調用失敗的情況。

空釋放

上面代碼中如果 C.Try() 是真正調用失敗,那下面多余的 C.Cancel() 調用會出現釋放并沒有鎖定資源的行為。這是因為當前服務無法判斷調用失敗是不是真的鎖定 C 資源了。如果不調用,實際上成功了,但由于網絡原因返回失敗了,這會導致 C 的資源被鎖定,一直得不到釋放。

空釋放在生產環境經常出現,服務在實現 TCC 事務 API 時,應支持空釋放的執行。

時序

上面代碼中如果 C.Try() 失敗,接著調用 C.Cancel() 操作。因為網絡原因,有可能會出現 C.Cancel() 請求會先到 C 服務,C.Try() 請求后到,這會導致空釋放問題,同時引起 C 的資源被鎖定,一直得不到釋放。

所以 C 服務應拒絕釋放資源之后的 Try() 操作。具體實現上,可以用唯一事務ID來區分第一次 Try() 還是釋放后的 Try()。

調用失敗

Cancel 、Confirm 在調用過程中,還是會存在失敗的情況,比如常見的網絡原因。

Cancel() 或 Confirm() 操作失敗都會導致資源被鎖定,一直得不到釋放。這種情況常見解決方案有:

阻塞式重試。但有同樣的問題,比如宕機、一直失敗的情況。寫入日志、隊列,然后有單獨的異步服務自動或人工介入處理。但一樣會有問題,寫日志或隊列時,會存在失敗的情況。

理論上來講非原子性、事務性的二段代碼,都會存在中間態,有中間態就會有失敗的可能性。

本地消息表

本地消息表最初是 ebay 提出的,它讓本地消息表與業務數據表處于同一個數據庫中,這樣就能利用本地事務來滿足事務特性。

具體做法是在本地事務中插入業務數據時,也插入一條消息數據。然后在做后續操作,如果其他操作成功,則刪除該消息;如果失敗則不刪除,異步監聽這個消息,不斷重試。

本地消息表是一個很好的思路,可以有多種使用方式:

配合MQ

示例偽代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
messageTx := tc.NewTransaction("order")
messageTxSql := tx.TryPlan("content")
 
m,err := db.InsertTx(sql,messageTxSql)
if err!=nil {
    return err
}
 
aErr := mq.Publish("B-Service-topic",m)
if aErr!=nil { // 推送到 MQ 失敗
    messageTx.Confirm() // 更新消息的狀態為 confirm
}else {
    messageTx.Cancel() // 刪除消息
}
 
// 異步處理 confirm 的消息,繼續推送
func OnMessage(task *Task){
   err := mq.Publish("B-Service-topic", task.Value())
   if err==nil {
     messageTx.Cancel()
   }
}

上面代碼中其 messageTxSql 是插入本地消息表的一段 SQL :

?
1
2
insert into `tcc_async_task` (`uid`,`name`,`value`,`status`)
values ('?','?','?','?')

它和業務 SQL 在同一個事務中去執行,要么成功,要么失敗。

成功則推送到隊列,推送成功,則調用 messageTx.Cancel() 刪除本地消息;推送失敗則標記消息為 confirm。本地消息表中 status 有 2 種狀態 tryconfirm, 無論哪種狀態在 OnMessage 都可以監聽到,從而發起重試。

本地事務保障消息和業務一定會寫入數據庫,此后的執行無論宕機還是網絡推送失敗,異步監聽都可以進行后續處理,從而保障了消息一定會推到 MQ。

而 MQ 則保障一定會到達消費者服務中,利用 MQ 的 QOS 策略,消費者服務一定能處理,或繼續投遞到下一個業務隊列中,從而保障了事務的完整性。

配合服務調用

示例偽代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
messageTx := tc.NewTransaction("order")
messageTxSql := tx.TryPlan("content")
 
body,err := db.InsertTx(sql,messageTxSql)
if err!=nil {
    return err
}
 
aErr := request.POST("B-Service",body)
if aErr!=nil { // 調用 B-Service 失敗
    messageTx.Confirm() // 更新消息的狀態為 confirm
}else {
    messageTx.Cancel() // 刪除消息
}
 
// 異步處理 confirm 或 try 的消息,繼續調用 B-Service
func OnMessage(task *Task){
  // request.POST("B-Service",body)
}

這是本地消息表 + 調用其他服務的例子,沒有 MQ 的引入。這種使用異步重試,并用本地消息表保障消息的可靠性,解決了阻塞式重試帶來的問題,在日常開發中比較常見。

如果本地沒有要寫 DB 的操作,可以只寫入本地消息表,同樣在 OnMessage中處理:

?
1
2
3
4
messageTx := tc.NewTransaction("order")
messageTx := tx.Try("content")
aErr := request.POST("B-Service",body)
// ....

消息過期

配置本地消息表的 TryConfirm 消息的處理器:

?
1
2
TCC.SetTryHandler(OnTryMessage())
TCC.SetConfirmHandler(OnConfirmMessage())

在消息處理函數中要判斷當前消息任務是否存在過久,比如一直重試了一小時,還是失敗,就考慮發郵件、短信、日志告警等方式,讓人工介入。

?
1
2
3
4
5
6
7
func OnConfirmMessage(task *tcc.Task) {
if time.Now().Sub(task.CreatedAt) > time.Hour {
    err := task.Cancel()  // 刪除該消息,停止重試。
   // doSomeThing() 告警,人工介入
    return
 }
}

Try 處理函數中,還要單獨判斷當前消息任務是否存在過短,因為 Try狀態的消息,可能才剛剛創建,還沒被確認提交或刪除。這會和正常業務邏輯的執行重復,意味著成功的調用,也會被重試;為盡量避免這種情況,可以檢測消息的創建時間是否很短,短的話可以跳過。

重試機制必然依賴下游 API 在業務邏輯上的冪等性,雖然不處理也可行,但設計上還是要盡量避免干擾正常的請求。

獨立消息服務

獨立消息服務是本地消息表的升級版,把本地消息表抽離成一個獨立的服務。所有操作之前先在消息服務添加個消息,后續操作成功則刪除消息,失敗則提交確認消息。

然后用異步邏輯去監聽消息,做對應的處理,和本地消息表的處理邏輯基本一致。但由于向消息服務添加消息,無法和本地操作放到一個事務里,所以會存在添加消息成功,后續失敗,則此時的消息就是個無用消息。

如下示例場景:

?
1
2
3
4
5
6
7
8
err := request.POST("Message-Service",body)
if err!=nil {
  return err
}
aErr := request.POST("B-Service",body)
if aErr!=nil {
  return aErr
}

這個無用的消息,需要消息服務去確認這個消息是否執行成功,沒有則刪除,有繼續執行后續邏輯。相比本地事務表 tryconfirm ,消息服務在前面多了一種狀態 prepare

MQ 事務

有些 MQ 的實現支持事務,比如 RocketMQ 。MQ 的事務可以看作獨立消息服務的一種具體實現,邏輯完全一致。

所有操作之前先在 MQ 投遞個消息,后續操作成功則 Confirm 確認提交消息,失敗則Cancel刪除消息。MQ 事務也會存在 prepare狀態,需要 MQ 的消費處理邏輯來確認業務是否成功。

總結

從分布式系統實踐中來看,要保障數據一致性的場景,必然要引入額外的機制處理。

TCC 的優點是作用于業務服務層,不依賴某個具體數據庫、不與具體框架耦合、資源鎖的粒度比較靈活,非常適用于微服務場景下。缺點是每個服務都要實現 3 個 API,對于業務侵入和改動較大,要處理各種失敗異常。開發者很難完整處理各種情況,找個成熟的框架可以大大降低成本,比如阿里的 Fescar。

本地消息表的優點是簡單、不依賴其他服務的改造、可以很好的配合服務調用和 MQ 一起使用,在大多業務場景下都比較實用。缺點是本地數據庫多了消息表,和業務表耦合在一起。文中本地消息表方式的示例,來源于作者寫的一個庫,有興趣的同學可以參考下 https://github.com/mushroomsir/tcc

MQ 事務和獨立消息服務的優點是抽離出一個公共的服務來解決事務問題,避免每個服務都有消息表和服務耦合在一起,增加服務自身的處理復雜性。缺點是支持事務的 MQ 很少;且每次操作前都先調用 API 添加個消息,會增加整體調用的延遲,在絕大多數正常響應的業務場景下,是一種多余的開銷。

TCC 參考:https://www.sofastack.tech/blog/seata-tcc-theory-design-realization/

MQ 事務參考:https://www.jianshu.com/p/eb571e4065ec

到此這篇關于詳解Java分布式事務的 6 種解決方案的文章就介紹到這了,更多相關java分布式事務內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/javastack/p/14926986.html

延伸 · 閱讀

精彩推薦
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7472021-02-04
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
主站蜘蛛池模板: 一级黄色免费 | 精国品产一区二区三区有限公司 | 成人国产精品色哟哟 | 在线观看国产一区二区 | 免费一级特黄毛片 | 毛片免费视频观看 | 久久精品在线免费观看 | 午夜在线视频一区二区三区 | 欧美成人精品不卡视频在线观看 | 日韩欧美视频一区二区三区 | 欧美黄色一级片在线观看 | 99久久久精品视频 | 久久久久久91 | 巨乳毛片 | 欧美成年私人网站 | 国产91九色在线播放 | 龙床上的呻吟高h | 一级毛片一区 | 欧美一级高清片在线 | 黄网站进入| 19禁国产精品福利视频 | 国产无遮挡一区二区三区毛片日本 | 成人毛片100免费观看 | 久久99精品久久久久久小说 | 蜜桃精品视频在线观看 | 国产91久久久久久 | 日本大片在线播放 | 一级看片免费视频 | 免费在线观看午夜视频 | 久久综合爱 | 久久久亚洲欧美综合 | av免费不卡国产观看 | 久久成人激情视频 | 欧美视频国产精品 | 神马久久精品综合 | 精国品产一区二区三区有限公司 | 成年免费在线视频 | 国av在线 | 日韩欧美动作影片 | 成人午夜免费在线观看 | 中国av免费观看 |