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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

分布式事务原理及实战seata(转自微信公众号 终码一生 )

發布時間:2025/3/19 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分布式事务原理及实战seata(转自微信公众号 终码一生 ) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

什么是分布式事務?

_____________________________________________________________________________

分布式對應的是單體架構,互聯網早起單體架構是非常流行的,好像是一個家族企業,大家在一個家里勞作,單體架構如下圖:

但是隨著業務的復雜度提高,大家族人手不夠,此時不得不招人,這樣逐漸演變出了分布式服務,互相協作,每個服務負責不同的業務,架構如下圖:

因此需要服務與服務之間的遠程協作才能完成事務,這種分布式系統環境下由不同的服務之間通過網絡遠程協作完成事務稱之為分布式事務,例如用戶注冊送積分 事務、創建訂單減庫存事務,銀行轉賬事務等都是分布式事務。

典型的場景就是微服務架構 微服務之間通過遠程調用完成事務操作。比如:訂單微服務庫存微服務,下單的同時訂單微服務請求庫存微服務減庫存。簡言之:跨JVM進程產生分布式事務

什么是CAP原則?

_____________________________________________________________________________

?CAP原則又叫CAP定理,同時又被稱作布魯爾定理(Brewer's theorem),指的是在一個分布式系統中,不可能同時滿足以下三點

一致性(Consistency)

指強一致性,在寫操作完成后開始的任何讀操作都必須返回該值,或者后續寫操作的結果。

也就是說,在一致性系統中,一旦客戶端將值寫入任何一臺服務器并獲得響應,那么之后client從其他任何服務器讀取的都是剛寫入的數據

一致性保證了不管向哪臺服務器寫入數據,其他的服務器能實時同步數據

可用性(Availability)

可用性(高可用)是指:每次向未崩潰的節點發送請求,總能保證收到響應數據(允許不是最新數據)

分區容忍性(Partition tolerance)

分布式系統在遇到任何網絡分區故障的時候,仍然能夠對外提供滿足一致性和可用性的服務,也就是說,服務器AB發送給對方的任何消息都是可以放棄的,也就是說A和B可能因為各種意外情況,導致無法成功進行同步,分布式系統要能容忍這種情況。除非整個網絡環境都發生了故障。

為什么只能在A和C之間做出取舍?

分布式系統中,必須滿足 CAP 中的 P,此時只能在 C/A 之間作出取舍。

如果選擇了CA,舍棄了P,說白了就是一個單體架構。

一致性有幾種分類?

_____________________________________________________________________________

CAP理論告訴我們只能在C、A之間選擇,在分布式事務的最終解決方案中一般選擇犧牲一致性來獲取可用性和分區容錯性。

這里的 “犧牲一致性” 并不是完全放棄數據的一致性,而是放棄強一致性而換取弱一致性

一致性可以分為以下三種:

  • 強一致性

  • 弱一致性

  • 最終一致性

強一致性

系統中的某個數據被成功更新后,后續任何對該數據的讀取操作都將得到更新后的值。

也稱為:原子一致性(Atomic Consistency)、線性一致性(Linearizable Consistency)

簡言之,在任意時刻,所有節點中的數據是一樣的。例如,對于關系型數據庫,要求更新過的數據能被后續的訪問都能看到,這是強一致性。

總結

  • 一個集群需要對外部提供強一致性,所以只要集群內部某一臺服務器的數據發生了改變,那么就需要等待集群內其他服務器的數據同步完成后,才能正常的對外提供服務。

  • 保證了強一致性,務必會損耗可用性

弱一致性

系統中的某個數據被更新后,后續對該數據的讀取操作可能得到更新后的值,也可能是更改前的值。

但即使過了不一致時間窗口這段時間后,后續對該數據的讀取也不一定是最新值。

所以說,可以理解為數據更新后,如果能容忍后續的訪問只能訪問到部分或者全部訪問不到,則是弱一致性。

例如12306買火車票,雖然最后看到還剩下幾張余票,但是只要選擇購買就會提示沒票了,這就是弱一致性。

最終一致性

是弱一致性的特殊形式,存儲系統保證在沒有新的更新的條件下,最終所有的訪問都是最后更新的值。

不保證在任意時刻任意節點上的同一份數據都是相同的,但是隨著時間的遷移,不同節點上的同一份數據總是在向趨同的方向變化。

簡單說,就是在一段時間后,節點間的數據會最終達到一致狀態。

總結

弱一致性即使過了不一致時間窗口,后續的讀取也不一定能保證一致,而最終一致過了不一致窗口后,后續的讀取一定一致。

什么是Base理論?

_____________________________________________________________________________

BASE理論是對CAP中的一致性和可用性進行一個權衡的結果,理論的核心思想就是:我們無法做到強一致,但每個應用都可以根據自身的業務特點,采用適當的方式來使系統達到最終一致性。

BA(Basic Available)基本可用

整個系統在某些不可抗力的情況下,仍然能夠保證“可用性”,即一定時間內仍然能夠返回一個明確的結果。這里是屬于基本可用。

基本可用和高可用的區別:

  • “一定時間”可以適當延長 當舉行大促(比如秒殺)時,響應時間可以適當延長

  • 給部分用戶返回一個降級頁面 給部分用戶直接返回一個降級頁面,從而緩解服務器壓力。但要注意,返回降級頁面仍然是返回明確結果。

S(Soft State)柔性狀態

稱為柔性狀態,是指允許系統中的數據存在中間狀態,并認為該中間狀態的存在不會影響系統的整體可用性,即允許系統不同節點的數據副本之間進行數據同步的過程存在延時。

E(Eventual Consisstency)最終一致性

同一數據的不同副本的狀態,可以不需要實時一致,但一定要保證經過一定時間后仍然是一致的。

分布式事務有哪幾種解決方案?

_____________________________________________________________________________

在分布式架構下,每個節點只知曉自己操作的失敗或者成功,無法得知其他節點的狀態。當一個事務跨多個節點時,為了保持事務的原子性與一致性,而引入一個協調者來統一掌控所有參與者的操作結果,并指示它們是否要把操作結果進行真正的提交或者回滾(rollback)。

2階段提交(2PC)

二階段提交協議(Two-phase Commit,即 2PC)是常用的分布式事務解決方案,即將事務的提交過程分為兩個階段來進行處理。

兩個階段分別為:

  • 準備階段

  • 提交階段

參與的角色:

  • 事務協調者(事務管理器):事務的發起者

  • 事務參與者(資源管理器):事務的執行者

準備階段(投票階段)

這是兩階段的第一段,這一階段只是準備階段,由事務的協調者發起詢問參與者是否可以提交事務,但是這一階段并未提交事務,流程圖如下圖:

準備提交階段

  • 協調者向所有參與者發送事務內容,詢問是否可以提交事務,并等待答復

  • 各參與者執行事務操作,將 undo 和 redo 信息記入事務日志中(但不提交事務)

  • 如參與者執行成功,給協調者反饋同意,否則反饋中止

  • 提交階段

    這一段階段屬于2PC的第二階段(提交 執行階段),協調者發起正式提交事務的請求,當所有參與者都回復同意時,則意味著完成事務,流程圖如下:

    提交事務階段

  • 協調者節點向所有參與者節點發出正式提交(commit)的請求。

  • 參與者節點正式完成操作,并釋放在整個事務期間內占用的資源。

  • 參與者節點向協調者節點發送ack完成消息。

  • 協調者節點收到所有參與者節點反饋的ack完成消息后,完成事務。

  • 但是如果任意一個參與者節點在第一階段返回的消息為終止,或者協調者節點在第一階段的詢問超時之前無法獲取所有參與者節點的響應消息時,那么這個事務將會被回滾,回滾的流程圖如下:

    ?回滾

  • 協調者節點向所有參與者節點發出回滾操作(rollback)的請求。

  • 參與者節點利用階段1寫入的undo信息執行回滾,并釋放在整個事務期間內占用的資源。

  • 參與者節點向協調者節點發送ack回滾完成消息。

  • 協調者節點受到所有參與者節點反饋的ack回滾完成消息后,取消事務。

  • 不管最后結果如何,第二階段都會結束當前事務。

    二階段提交的事務正常提交的完整流程如下圖:

    ?事務正常提交完整流程

    二階段提交事務回滾的完整流程如下圖:

    事務回滾完整流程

    舉個百米賽跑的例子來具體描述下2PC的流程:學校運動會,有三個同學,分別是A,B,C,2PC流程如下:

    • 裁判:A同學準備好了嗎?準備進入第一賽道....

    • 裁判:B同學準備好了嗎?準備進入第一賽道....

    • 裁判:C同學準備好了嗎?準備進入第一賽道....

    • 如果有任意一個同學沒準備好,則裁判下達回滾指令

    • 如果裁判收到了所有同學的OK回復,則再次下令跑......

    • 裁判:1,2,3 跑............

    • A同學沖刺到終點,匯報給裁判

    • B,C同學沖刺失敗,匯報給裁判

    2PC的缺點

    二階段提交看起來確實能夠提供原子性的操作,但是不幸的是,二階段提交還是有幾個缺點的:

    • 性能問題:執行過程中,所有參與節點都是事務阻塞型的。當參與者占有公共資源時,其他第三方節點訪問公共資源不得不處于阻塞狀態。

    • 可靠性問題:參與者發生故障。協調者需要給每個參與者額外指定超時機制,超時后整個事務失敗。協調者發生故障。參與者會一直阻塞下去。需要額外的備機進行容錯。

    • 數據一致性問題:二階段無法解決的問題:協調者在發出commit消息之后宕機,而唯一接收到這條消息的參與者同時也宕機了。那么即使協調者通過選舉協議產生了新的協調者,這條事務的狀態也是不確定的,沒人知道事務是否被已經提交。

    • 實現復雜:犧牲了可用性,對性能影響較大,不適合高并發高性能場景。

    2PC的優點

    • 盡量保證了數據的強一致,適合對數據強一致要求很高的關鍵領域。(其實也不能100%保證強一致)

    3階段提交(3PC)

    三階段提交協議,是二階段提交協議的改進版本,三階段提交有兩個改動點。

    • 在協調者和參與者中都引入超時機制

    • 在第一階段和第二階段中插入一個準備階段。保證了在最后提交階段之前各參與節點的狀態是一致的。

    也就是說,除了引入超時機制之外,3PC把2PC的準備階段再次一分為二,這樣三階段提交就有CanCommit、PreCommit、DoCommit三個階段。處理流程如下:

    3PC時序圖

    階段一:CanCommit階段

    3PC的CanCommit階段其實和2PC的準備階段很像。協調者向參與者發送commit請求,參與者如果可以提交就返回Yes響應,否則返回No響應。

    • 事務詢問:協調者向所有參與者發出包含事務內容的?canCommit?請求,詢問是否可以提交事務,并等待所有參與者答復。

    • 響應反饋:參與者收到?canCommit?請求后,如果認為可以執行事務操作,則反饋 yes 并進入預備狀態,否則反饋 no。

    CanCommit階段流程如下圖:

    CanCommit階段

    階段二:PreCommit階段

    協調者根據參與者的反應情況來決定是否可以進行事務的PreCommit操作。根據響應情況,有以下兩種可能。

    • 假如所有參與者均反饋?yes,協調者預執行事務。

    • 發送預提交請求 :協調者向參與者發送PreCommit請求,并進入準備階段

    • 事務預提交 :參與者接收到PreCommit請求后,會執行事務操作,并將undo和redo信息記錄到事務日志中(但不提交事務)

    • 響應反饋 :如果參與者成功的執行了事務操作,則返回ACK響應,同時開始等待最終指令。

    PreCommit

    • 假如有任何一個參與者向協調者發送了No響應,或者等待超時之后,協調者都沒有接到參與者的響應,那么就執行事務的中斷。

    • 發送中斷請求 :協調者向所有參與者發送abort請求。

    • 中斷事務 :參與者收到來自協調者的abort請求之后(或超時之后,仍未收到協調者的請求),執行事務的中斷。

    PreCommit

    階段三:doCommit階段

    該階段進行真正的事務提交,也可以分為以下兩種情況。

    進入階段 3 后,無論協調者出現問題,或者協調者與參與者網絡出現問題,都會導致參與者無法接收到協調者發出的 do Commit 請求或 abort 請求。此時,參與者都會在等待超時之后,繼續執行事務提交。

    • 執行提交

    • 發送提交請求 協調接收到參與者發送的ACK響應,那么他將從預提交狀態進入到提交狀態。并向所有參與者發送doCommit請求。

    • 事務提交 參與者接收到doCommit請求之后,執行正式的事務提交。并在完成事務提交之后釋放所有事務資源。

    • 響應反饋 事務提交完之后,向協調者發送ack響應。

    • 完成事務 協調者接收到所有參與者的ack響應之后,完成事務。

    docommit-提交事務

    • 中斷事務:任何一個參與者反饋 no,或者等待超時后協調者尚無法收到所有參與者的反饋,即中斷事務

    • 發送中斷請求 如果協調者處于工作狀態,向所有參與者發出 abort 請求

    • 事務回滾 參與者接收到abort請求之后,利用其在階段二記錄的undo信息來執行事務的回滾操作,并在完成回滾之后釋放所有的事務資源。

    • 反饋結果 參與者完成事務回滾之后,向協調者反饋ACK消息

    • 中斷事務 協調者接收到參與者反饋的ACK消息之后,執行事務的中斷。

    docommit-中斷事務

    優點

    相比二階段提交,三階段提交降低了阻塞范圍,在等待超時后協調者或參與者會中斷事務。避免了協調者單點問題,階段 3 中協調者出現問題時,參與者會繼續提交事務。

    缺點

    數據不一致問題依然存在,當在參與者收到?preCommit?請求后等待?doCommit?指令時,此時如果協調者請求中斷事務,而協調者無法與參與者正常通信,會導致參與者繼續提交事務,造成數據不一致。

    TCC(事務補償)

    TCC(Try Confirm Cancel)方案是一種應用層面侵入業務的兩階段提交。是目前最火的一種柔性事務方案,其核心思想是:針對每個操作,都要注冊一個與其對應的確認和補償(撤銷)操作

    TCC分為兩個階段,分別如下:

    • 第一階段:Try(嘗試),主要是對業務系統做檢測及資源預留?(加鎖,鎖住資源)

    • 第二階段:本階段根據第一階段的結果,決定是執行confirm還是cancel

    • Confirm(確認):執行真正的業務(執行業務,釋放鎖)

    • Cancle(取消):是預留資源的取消(出問題,釋放鎖)

    TCC

    為了方便理解,下面以電商下單為例進行方案解析,這里把整個過程簡單分為扣減庫存,訂單創建 2 個步驟,庫存服務和訂單服務分別在不同的服務器節點上。

    假設商品庫存為 100,購買數量為 2,這里檢查和更新庫存的同時,凍結用戶購買數量的庫存,同時創建訂單,訂單狀態為待確認。

    ①Try 階段

    TCC 機制中的 Try 僅是一個初步操作,它和后續的確認一起才能真正構成一個完整的業務邏輯,這個階段主要完成:

    • 完成所有業務檢查( 一致性 ) 。

    • 預留必須業務資源( 準隔離性 ) 。

    • Try 嘗試執行業務。

    Try階段

    ②Confirm / Cancel 階段

    根據?Try?階段服務是否全部正常執行,繼續執行確認操作(Confirm)或取消操作(Cancel)。

    Confirm 和 Cancel 操作滿足冪等性,如果 Confirm 或 Cancel 操作執行失敗,將會不斷重試直到執行完成。

    Confirm:當 Try 階段服務全部正常執行, 執行確認業務邏輯操作,業務如下圖:

    Try->Confirm

    這里使用的資源一定是 Try 階段預留的業務資源。在 TCC 事務機制中認為,如果在 Try 階段能正常的預留資源,那 Confirm 一定能完整正確的提交。

    Confirm 階段也可以看成是對 Try 階段的一個補充,Try+Confirm 一起組成了一個完整的業務邏輯。

    Cancel:當 Try 階段存在服務執行失敗, 進入 Cancel 階段,業務如下圖:

    Try-Cancel

    Cancel 取消執行,釋放 Try 階段預留的業務資源,上面的例子中,Cancel 操作會把凍結的庫存釋放,并更新訂單狀態為取消。

    最終一致性保證

    • TCC 事務機制以初步操作(Try)為中心的,確認操作(Confirm)和取消操作(Cancel)都是圍繞初步操作(Try)而展開。因此,Try 階段中的操作,其保障性是最好的,即使失敗,仍然有取消操作(Cancel)可以將其執行結果撤銷。

    • Try階段執行成功并開始執行?Confirm階段時,默認?Confirm階段是不會出錯的。也就是說只要Try成功,Confirm一定成功(TCC設計之初的定義) 。

    • Confirm與Cancel如果失敗,由TCC框架進行==重試==補償

    • 存在極低概率在CC環節徹底失敗,則需要定時任務或人工介入

    方案總結

    TCC 事務機制相對于傳統事務機制(X/Open XA),TCC 事務機制相比于上面介紹的 XA 事務機制,有以下優點:

    • 性能提升:具體業務來實現控制資源鎖的粒度變小,不會鎖定整個資源。

    • 數據最終一致性:基于 Confirm 和 Cancel 的冪等性,保證事務最終完成確認或者取消,保證數據的一致性。

    • 可靠性:解決了 XA 協議的協調者單點故障問題,由主業務方發起并控制整個業務活動,業務活動管理器也變成多點,引入集群。

    缺點:

    • TCC 的 Try、Confirm 和 Cancel 操作功能要按具體業務來實現,業務耦合度較高,提高了開發成本。

    本地消息表

    本地消息表的方案最初是由 eBay 提出,核心思路是將分布式事務拆分成本地事務進行處理。

    角色:

    • 事務主動方

    • 事務被動方

    通過在事務主動發起方額外新建事務消息表,事務發起方處理業務和記錄事務消息在本地事務中完成,輪詢事務消息表的數據發送事務消息,事務被動方基于消息中間件消費事務消息表中的事務。

    這樣可以避免以下兩種情況導致的數據不一致性:

    • 業務處理成功、事務消息發送失敗

    • 業務處理失敗、事務消息發送成功

    整體的流程如下圖:

    本地消息表

    上圖中整體的處理步驟如下:

    • ①:事務主動方在同一個本地事務中處理業務和寫消息表操作

    • ②:事務主動方通過消息中間件,通知事務被動方處理事務通知事務待消息。消息中間件可以基于 Kafka、RocketMQ 消息隊列,事務主動方主動寫消息到消息隊列,事務消費方消費并處理消息隊列中的消息。

    • ③:事務被動方通過消息中間件,通知事務主動方事務已處理的消息。

    • ④:事務主動方接收中間件的消息,更新消息表的狀態為已處理。

    一些必要的容錯處理如下:

    • 當①處理出錯,由于還在事務主動方的本地事務中,直接回滾即可

    • 當②、③處理出錯,由于事務主動方本地保存了消息,只需要輪詢消息重新通過消息中間件發送,事務被動方重新讀取消息處理業務即可。

    • 如果是業務上處理失敗,事務被動方可以發消息給事務主動方回滾事務

    • 如果事務被動方已經消費了消息,事務主動方需要回滾事務的話,需要發消息通知事務主動方進行回滾事務。

    優點

    • 從應用設計開發的角度實現了消息數據的可靠性,消息數據的可靠性不依賴于消息中間件,弱化了對 MQ 中間件特性的依賴。

    • 方案輕量,容易實現。

    缺點

    • 與具體的業務場景綁定,耦合性強,不可公用。

    • 消息數據與業務數據同庫,占用業務系統資源。

    • 業務系統在使用關系型數據庫的情況下,消息服務性能會受到關系型數據庫并發性能的局限。

    MQ事務方案(可靠消息事務)

    基于 MQ 的分布式事務方案其實是對本地消息表的封裝,將本地消息表基于 MQ 內部,其他方面的協議基本與本地消息表一致。

    MQ事務方案整體流程和本地消息表的流程很相似,如下圖:

    MQ事務方案

    從上圖可以看出和本地消息表方案唯一不同就是將本地消息表存在了MQ內部,而不是業務數據庫中。

    那么MQ內部的處理尤為重要,下面主要基于 RocketMQ 4.3 之后的版本介紹 MQ 的分布式事務方案。

    在本地消息表方案中,保證事務主動方發寫業務表數據和寫消息表數據的一致性是基于數據庫事務,RocketMQ 的事務消息相對于普通 MQ提供了 2PC 的提交接口,方案如下:

    正常情況:事務主動方發消息

    事務主動方發消息

    這種情況下,事務主動方服務正常,沒有發生故障,發消息流程如下:

    • 步驟①:發送方向 MQ 服務端(MQ Server)發送 half 消息。

    • 步驟②:MQ Server 將消息持久化成功之后,向發送方 ack 確認消息已經發送成功。

    • 步驟③:發送方開始執行本地事務邏輯。

    • 步驟④:發送方根據本地事務執行結果向 MQ Server 提交二次確認(commit 或是 rollback)。

    • 步驟⑤:MQ Server 收到 commit 狀態則將半消息標記為可投遞,訂閱方最終將收到該消息;MQ Server 收到 rollback 狀態則刪除半消息,訂閱方將不會接受該消息。

    異常情況:事務主動方消息恢復

    事務主動方消息恢復

    在斷網或者應用重啟等異常情況下,圖中 4 提交的二次確認超時未到達 MQ Server,此時處理邏輯如下:

    • 步驟⑤:MQ Server 對該消息發起消息回查。

    • 步驟⑥:發送方收到消息回查后,需要檢查對應消息的本地事務執行的最終結果。

    • 步驟⑦:發送方根據檢查得到的本地事務的最終狀態再次提交二次確認。

    • 步驟⑧:MQ Server基于 commit/rollback 對消息進行投遞或者刪除。

    優點

    相比本地消息表方案,MQ 事務方案優點是:

    • 消息數據獨立存儲 ,降低業務系統與消息系統之間的耦合。

    • 吞吐量大于使用本地消息表方案。

    缺點

    • 一次消息發送需要兩次網絡請求(half 消息 + commit/rollback 消息) 。

    • 業務處理服務需要實現消息狀態回查接口。

    最大努力通知

    最大努力通知也稱為定期校對,是對MQ事務方案的進一步優化。它在事務主動方增加了消息校對的接口,如果事務被動方沒有接收到消息,此時可以調用事務主動方提供的消息校對的接口主動獲取。

    最大努力通知的整體流程如下圖:

    最大努力通知

    在可靠消息事務中,事務主動方需要將消息發送出去,并且消息接收方成功接收,這種可靠性發送是由事務主動方保證的;

    但是最大努力通知,事務主動方盡最大努力(重試,輪詢....)將事務發送給事務接收方,但是仍然存在消息接收不到,此時需要事務被動方主動調用事務主動方的消息校對接口查詢業務消息并消費,這種通知的可靠性是由事務被動方保證的。

    最大努力通知適用于業務通知類型,例如微信交易的結果,就是通過最大努力通知方式通知各個商戶,既有回調通知,也有交易查詢接口。

    Saga 事務

    Saga 事務源于 1987 年普林斯頓大學的 Hecto 和 Kenneth 發表的如何處理 long lived transaction(長活事務)論文。

    Saga 事務核心思想是將長事務拆分為多個本地短事務,由 Saga 事務協調器協調,如果正常結束那就正常完成,如果某個步驟失敗,則根據相反順序一次調用補償操作。

    Saga 事務基本協議如下:

    • 每個 Saga 事務由一系列冪等的有序子事務(sub-transaction)?Ti?組成。

    • 每個?Ti?都有對應的冪等補償動作?Ci,補償動作用于撤銷?Ti?造成的結果。

    TCC事務補償機制有一個預留(Try)動作,相當于先報存一個草稿,然后才提交;Saga事務沒有預留動作,直接提交。

    對于事務異常,Saga提供了兩種恢復策略,分別如下:

    向后恢復(backward recovery)

    在執行事務失敗時,補償所有已完成的事務,是“一退到底”的方式。如下圖:

    向后恢復

    從上圖可知事務執行到了支付事務T3,但是失敗了,因此事務回滾需要從C3,C2,C1依次進行回滾補償。

    對應的執行順序為:T1,T2,T3,C3,C2,C1

    這種做法的效果是撤銷掉之前所有成功的子事務,使得整個 Saga 的執行結果撤銷。

    向前恢復(forward recovery)

    也稱之為:勇往直前,對于執行不通過的事務,會嘗試重試事務,這里有一個假設就是每個子事務最終都會成功。

    流程如下圖:

    向前恢復

    適用于必須要成功的場景,事務失敗了重試,不需要補償。

    Saga事務有兩種不同的實現方式,分別如下:

    • 命令協調(Order Orchestrator)

    • 事件編排(Event Choreographyo)

    命令協調

    中央協調器(Orchestrator,簡稱 OSO)以命令/回復的方式與每項服務進行通信,全權負責告訴每個參與者該做什么以及什么時候該做什么。整體流程如下圖:

    命令協調

    上圖步驟如下:

    • 事務發起方的主業務邏輯請求 OSO 服務開啟訂單事務

    • OSO 向庫存服務請求扣減庫存,庫存服務回復處理結果。

    • OSO 向訂單服務請求創建訂單,訂單服務回復創建結果。

    • OSO 向支付服務請求支付,支付服務回復處理結果。

    • 主業務邏輯接收并處理 OSO 事務處理結果回復。

    中央協調器必須事先知道執行整個訂單事務所需的流程(例如通過讀取配置)。如果有任何失敗,它還負責通過向每個參與者發送命令來撤銷之前的操作來協調分布式的回滾。

    基于中央協調器協調一切時,回滾要容易得多,因為協調器默認是執行正向流程,回滾時只要執行反向流程即可。

    事件編排

    沒有中央協調器(沒有單點風險)時,每個服務產生并觀察其他服務的事件,并決定是否應采取行動。

    在事件編排方法中,第一個服務執行一個事務,然后發布一個事件。該事件被一個或多個服務進行監聽,這些服務再執行本地事務并發布(或不發布)新的事件。

    當最后一個服務執行本地事務并且不發布任何事件時,意味著分布式事務結束,或者它發布的事件沒有被任何 Saga 參與者聽到都意味著事務結束。

    事件編排

    上圖步驟如下:

    • 事務發起方的主業務邏輯發布開始訂單事件。

    • 庫存服務監聽開始訂單事件,扣減庫存,并發布庫存已扣減事件。

    • 訂單服務監聽庫存已扣減事件,創建訂單,并發布訂單已創建事件。

    • 支付服務監聽訂單已創建事件,進行支付,并發布訂單已支付事件。

    • 主業務邏輯監聽訂單已支付事件并處理。

    事件/編排是實現 Saga 模式的自然方式,它很簡單,容易理解,不需要太多的代碼來構建。如果事務涉及 2 至 4 個步驟,則可能是非常合適的。

    優點

    命令協調設計的優點如下:

    • 服務之間關系簡單,避免服務之間的循環依賴關系,因為 Saga 協調器會調用 Saga 參與者,但參與者不會調用協調器。

    • 程序開發簡單,只需要執行命令/回復(其實回復消息也是一種事件消息),降低參與者的復雜性。

    • 易維護擴展,在添加新步驟時,事務復雜性保持線性,回滾更容易管理,更容易實施和測試。

    事件/編排設計優點如下:

    • 避免中央協調器單點故障風險。

    • 當涉及的步驟較少服務開發簡單,容易實現。

    缺點

    命令協調設計缺點如下:

    • 中央協調器容易處理邏輯容易過于復雜,導致難以維護。

    • 存在協調器單點故障風險。

    事件/編排設計缺點如下:

    • 服務之間存在循環依賴的風險。

    • 當涉及的步驟較多,服務間關系混亂,難以追蹤調測。

    由于 Saga 模型中沒有 Prepare 階段,因此事務間不能保證隔離性。

    當多個 Saga 事務操作同一資源時,就會產生更新丟失、臟數據讀取等問題,這時需要在業務層控制并發,例如:在應用層面加鎖,或者應用層面預先凍結資源。

    總結

    總結一下各個方案的常見的使用場景:

    • 2PC/3PC:依賴于數據庫,能夠很好的提供強一致性和強事務性,但相對來說延遲比較高,比較適合傳統的單體應用,在同一個方法中存在跨庫操作的情況,不適合高并發和高性能要求的場景。

    • TCC:適用于執行時間確定且較短,實時性要求高,對數據一致性要求高,比如互聯網金融企業最核心的三個服務:交易、支付、賬務。

    • 本地消息表/MQ 事務:都適用于事務中參與方支持操作冪等,對一致性要求不高,業務上能容忍數據不一致到一個人工檢查周期,事務涉及的參與方、參與環節較少,業務上有對賬/校驗系統兜底。

    • Saga 事務:由于 Saga 事務不能保證隔離性,需要在業務層控制并發,適合于業務場景事務并發操作同一資源較少的情況。Saga 相比缺少預提交動作,導致補償動作的實現比較麻煩,例如業務是發送短信,補償動作則得再發送一次短信說明撤銷,用戶體驗比較差。Saga 事務較適用于補償動作容易處理的場景。

    什么是Seata?

    _____________________________________________________________________________

    上面講了這么多的分布式事務的理論知識,都沒看到一個落地的實現,這不是吹牛逼嗎?

    Seata 是一款開源的分布式事務解決方案,致力于提供高性能和簡單易用的分布式事務服務。Seata 將為用戶提供了?ATTCCSAGA?和?XA?事務模式,為用戶打造一站式的分布式解決方案。

    • 對業務無侵入:即減少技術架構上的微服務化所帶來的分布式事務問題對業務的侵入

    • 高性能:減少分布式事務解決方案所帶來的性能消耗

    官方文檔:https://seata.io/zh-cn/index.html

    seata的幾種術語:

    • TC(Transaction Coordinator):事務協調者。管理全局的分支事務的狀態,用于全局性事務的提交和回滾。

    • TM(Transaction Manager):事務管理者。用于開啟、提交或回滾事務。

    • RM(Resource Manager):資源管理器。用于分支事務上的資源管理,向?TC?注冊分支事務,上報分支事務的狀態,接收?TC?的命令來提交或者回滾分支事務。

    AT模式

    _____________________________________________________________________________

    seata目前支持多種事務模式,分別有ATTCCSAGA?和?XA?,文章篇幅有限,今天只講常用的AT模式。

    AT模式的特點就是對業務無入侵式,整體機制分二階段提交(2PC)

    • 一階段:業務數據和回滾日志記錄在同一個本地事務中提交,釋放本地鎖和連接資源。

    • 二階段:

    • 提交異步化,非常快速地完成

    • 回滾通過一階段的回滾日志進行反向補償。

    在 AT 模式下,用戶只需關注自己的業務SQL,用戶的業務SQL?作為一階段,Seata 框架會自動生成事務的二階段提交和回滾操作。

    一個典型的分布式事務過程:

    • TM 向 TC 申請開啟一個全局事務,全局事務創建成功并生成一個全局唯一的 XID;

    • XID 在微服務調用鏈路的上下文中傳播;

    • RM 向 TC 注冊分支事務,將其納入 XID 對應全局事務的管轄;

    • TM 向 TC 發起針對 XID 的全局提交或回滾決議;

    • TC 調度 XID 下管轄的全部分支事務完成提交或回滾請求。

      搭建Seata TC協調者?

    _____________________________________________________________________________

    seata的協調者其實就是阿里開源的一個服務,我們只需要下載并且啟動它。

    下載地址:http://seata.io/zh-cn/blog/download.html

    陳某下載的版本是1.3.0,各位最好和我版本一致,這樣不會出現莫名的BUG。

    下載完成后,直接解壓即可。但是此時還不能直接運行,還需要做一些配置。

    創建TC所需要的表

    TC運行需要將事務的信息保存在數據庫,因此需要創建一些表,找到seata-1.3.0源碼的script\server\db這個目錄,將會看到以下SQL文件:

    陳某使用的是Mysql數據庫,因此直接運行mysql.sql這個文件中的sql語句,創建的三張表如下圖:

    修改TC的注冊中心

    找到seata-server-1.3.0\seata\conf這個目錄,其中有一個registry.conf文件,其中配置了TC的注冊中心和配置中心。

    默認的注冊中心是file形式,實際使用中肯定不能使用,需要改成Nacos形式,改動的地方如下圖:

    需要改動的地方如下:

    • type:改成nacos,表示使用nacos作為注冊中心

    • application:服務的名稱

    • serverAddr:nacos的地址

    • group:分組

    • namespace:命名空間

    • username:用戶名

    • password:密碼

    最后這份文件都會放在項目源碼的根目錄下,源碼下載方式見文末

    修改TC的配置中心

    TC的配置中心默認使用的也是file形式,當然要是用nacos作為配置中心了。

    直接修改registry.conf文件,需要改動的地方如下圖:

    需要改動的地方如下:

    • type:改成nacos,表示使用nacos作為配置中心

    • serverAddr:nacos的地址

    • group:分組

    • namespace:命名空間

    • username:用戶名

    • password:密碼

    上述配置修改好之后,在TC啟動的時候將會自動讀取nacos的配置。

    那么問題來了:TC需要存儲到Nacos中的配置都哪些,如何推送過去?

    在seata-1.3.0\script\config-center中有一個config.txt文件,其中就是TC所需要的全部配置。

    在seata-1.3.0\script\config-center\nacos中有一個腳本nacos-config.sh則是將config.txt中的全部配置自動推送到nacos中,運行下面命令(windows可以使用git bash運行):

    #?-h?主機,你可以使用localhost,-p?端口號?你可以使用8848,-t?命名空間ID,-u?用戶名,-p?密碼 $?sh?nacos-config.sh?-h?127.0.0.1?-p?8080?-g?SEATA_GROUP?-t?7a7581ef-433d-46f3-93f9-5fdc18239c65?-u?nacos?-w?nacos

    推送成功則可以在Nacos中查詢到所有的配置,如下圖:

    修改TC的數據庫連接信息

    TC是需要使用數據庫存儲事務信息的,那么如何修改相關配置呢?

    上一節的內容已經將所有的配置信息都推送到了Nacos中,TC啟動時會從Nacos中讀取,因此我們修改也需要在Nacos中修改。

    需要修改的配置如下:

    ## 采用db的存儲形式 store.mode=db ## druid數據源 store.db.datasource=druid ## mysql數據庫 store.db.dbType=mysql ## mysql驅動 store.db.driverClassName=com.mysql.jdbc.Driver ## TC的數據庫url store.db.url=jdbc:mysql://127.0.0.1:3306/seata_server?useUnicode=true ## 用戶名 store.db.user=root ## 密碼 store.db.password=Nov2014

    在nacos中搜索上述的配置,直接修改其中的值,比如修改store.mode,如下圖:

    當然Seata還支持Redis作為TC的數據庫,只需要改動以下配置即可:

    store.mode=redis store.redis.host=127.0.0.1 store.redis.port=6379 store.redis.password=123456

    啟動TC

    按照上述步驟全部配置成功后,則可以啟動TC,在seata-server-1.3.0\seata\bin目錄下直接點擊seata-server.bat(windows)運行。

    啟動成功后,在Nacos的服務列表中則可以看到TC已經注冊進入,如下圖:

    至此,Seata的TC就啟動完成了............

    Seata客戶端搭建(RM)

    _____________________________________________________________________________

    上述已經將Seata的服務端(TC)搭建完成了,下面就以電商系統為例介紹一下如何編碼實現分布式事務。

    用戶購買商品的業務邏輯。整個業務邏輯由3個微服務提供支持:

    • 倉儲服務:對給定的商品扣除倉儲數量。

    • 訂單服務:根據采購需求創建訂單。

    • 帳戶服務:從用戶帳戶中扣除余額。

    需要了解的知識:Nacos和openFeign,有不清楚的可以看我的前兩章教程,如下:

    • 五十五張圖告訴你微服務的靈魂擺渡者Nacos究竟有多強?

    • openFeign奪命連環9問,這誰受得了?

    倉儲服務搭建

    陳某整個教程使用的都是同一個聚合項目,關于Spring Cloud版本有不清楚的可以看我第一篇文章的說明。

    添加依賴

    新建一個seata-storage9020項目,新增依賴如下:

    由于使用的springCloud Alibaba依賴版本是2.2.1.RELEASE,其中自帶的seata版本是1.1.0,但是我們Seata服務端使用的版本是1.3.0,因此需要排除原有的依賴,重新添加1.3.0的依賴。

    注意:seata客戶端的依賴版本必須要和服務端一致。

    創建數據庫

    創建一個數據庫seata-storage,其中新建兩個表:

    • storage:庫存的業務表,SQL如下:

    CREATE?TABLE?`storage`??(`id`?bigint(11)?NOT?NULL?AUTO_INCREMENT,`name`?varchar(100)?CHARACTER?SET?utf8mb4?COLLATE?utf8mb4_general_ci?NULL?DEFAULT?NULL,`num`?bigint(11)?NULL?DEFAULT?NULL?COMMENT?'數量',`create_time`?datetime(0)?NULL?DEFAULT?NULL,`price`?bigint(10)?NULL?DEFAULT?NULL?COMMENT?'單價,單位分',PRIMARY?KEY?(`id`)?USING?BTREE )?ENGINE?=?InnoDB?AUTO_INCREMENT?=?2?CHARACTER?SET?=?utf8mb4?COLLATE?=?utf8mb4_general_ci?ROW_FORMAT?=?Compact;INSERT?INTO?`storage`?VALUES?(1,?'碼猿技術專欄',?1000,?'2021-10-15?22:32:40',?100);
    • undo_log:回滾日志表,這是Seata要求必須有的,每個業務庫都應該創建一個,SQL如下:

    CREATE?TABLE?`undo_log`??(`branch_id`?bigint(20)?NOT?NULL?COMMENT?'branch?transaction?id',`xid`?varchar(100)?CHARACTER?SET?utf8?COLLATE?utf8_general_ci?NOT?NULL?COMMENT?'global?transaction?id',`context`?varchar(128)?CHARACTER?SET?utf8?COLLATE?utf8_general_ci?NOT?NULL?COMMENT?'undo_log?context,such?as?serialization',`rollback_info`?longblob?NOT?NULL?COMMENT?'rollback?info',`log_status`?int(11)?NOT?NULL?COMMENT?'0:normal?status,1:defense?status',`log_created`?datetime(6)?NOT?NULL?COMMENT?'create?datetime',`log_modified`?datetime(6)?NOT?NULL?COMMENT?'modify?datetime',UNIQUE?INDEX?`ux_undo_log`(`xid`,?`branch_id`)?USING?BTREE )?ENGINE?=?InnoDB?CHARACTER?SET?=?utf8?COLLATE?=?utf8_general_ci?COMMENT?=?'AT?transaction?mode?undo?table'?ROW_FORMAT?=?Compact;

    配置seata相關配置

    對于Nacos、Mysql數據源等相關信息就省略了,項目源碼中都有。主要講一下seata如何配置,詳細配置如下:

    spring:application:##?指定服務名稱,在nacos中的名字name:?seata-storage ##?客戶端seata的相關配置 seata:##?是否開啟seata,默認trueenabled:?trueapplication-id:?${spring.application.name}##?seata事務組的名稱,一定要和config.tx(nacos)中配置的相同tx-service-group:?${spring.application.name}-tx-group##?配置中心的配置config:##?使用類型nacostype:?nacos##?nacos作為配置中心的相關配置,需要和server在同一個注冊中心下nacos:##?命名空間,需要server端(registry和config)、nacos配置client端(registry和config)保持一致namespace:?7a7581ef-433d-46f3-93f9-5fdc18239c65##?地址server-addr:?localhost:8848##?組,?需要server端(registry和config)、nacos配置client端(registry和config)保持一致group:?SEATA_GROUP##?用戶名和密碼username:?nacospassword:?nacosregistry:type:?nacosnacos:##?這里的名字一定要和seata服務端中的名稱相同,默認是seata-serverapplication:?seata-server##?需要server端(registry和config)、nacos配置client端(registry和config)保持一致group:?SEATA_GROUPnamespace:?7a7581ef-433d-46f3-93f9-5fdc18239c65username:?nacospassword:?nacosserver-addr:?localhost:8848

    以上配置注釋已經很清楚,這里著重強調以下幾點:

    • 客戶端seata中的nacos相關配置要和服務端相同,比如地址、命名空間..........

    • tx-service-group:這個屬性一定要注意,這個一定要和服務端的配置一致,否則不生效;比如上述配置中的,就要在nacos中新增一個配置service.vgroupMapping.seata-storage-tx-group=default,如下圖:

    注意:seata-storage-tx-group僅僅是后綴,要記得添加配置的時候要加上前綴service.vgroupMapping.

    扣減庫存的接口

    邏輯很簡單,這里僅僅是做了減庫存的操作,代碼如下:

    這里的接口并沒有不同,還是使用@Transactional開啟了本地事務,并沒有涉及到分布式事務。

    到這里倉儲服務搭建好了..............

    賬戶服務搭建

    搭建完了倉儲服務,賬戶服務搭建很類似了。

    添加依賴

    新建一個seata-account9021服務,這里的依賴和倉儲服務的依賴相同,直接復制

    創建數據庫

    創建一個seata-account數據庫,其中新建了兩個表:

    • account:賬戶業務表,SQL如下:

    CREATE?TABLE?`account`??(`id`?bigint(11)?NOT?NULL,`user_id`?varchar(32)?CHARACTER?SET?utf8mb4?COLLATE?utf8mb4_general_ci?NULL?DEFAULT?NULL?COMMENT?'用戶userId',`money`?bigint(11)?NULL?DEFAULT?NULL?COMMENT?'余額,單位分',`create_time`?datetime(0)?NULL?DEFAULT?NULL,PRIMARY?KEY?(`id`)?USING?BTREE )?ENGINE?=?InnoDB?CHARACTER?SET?=?utf8mb4?COLLATE?=?utf8mb4_general_ci?ROW_FORMAT?=?Compact;INSERT?INTO?`account`?VALUES?(1,?'abc123',?1000,?'2021-10-19?17:49:53');
    • undo_log:回滾日志表,同倉儲服務

    配置seata相關配置

    Seata相關配置和倉儲服務相同,只不過需要在nacos中添加一個service.vgroupMapping.seata-account-tx-group=default,如下圖:

    扣減余額的接口

    具體邏輯自己完善,這里我直接扣減余額,代碼如下:

    依然沒有涉及到分布式事務,還是使用@Transactional開啟了本地事務,是不是很爽............

    訂單服務搭建(TM)

    這里為了節省篇幅,陳某直接使用訂單服務作為TM,下單、減庫存、扣款整個流程都在訂單服務中實現。

    添加依賴

    新建一個seata-order9022服務,這里需要添加的依賴如下:

    • Nacos服務發現的依賴

    • seata的依賴

    • openFeign的依賴,由于要調用賬戶、倉儲的微服務,因此需要額外添加一個openFeign的依賴

    創建數據庫

    新建一個seata_order數據庫,其中新建兩個表,如下:

    • t_order:訂單的業務表

    CREATE?TABLE?`t_order`??(`id`?bigint(11)?NOT?NULL?AUTO_INCREMENT,`product_id`?bigint(11)?NULL?DEFAULT?NULL?COMMENT?'商品Id',`num`?bigint(11)?NULL?DEFAULT?NULL?COMMENT?'數量',`user_id`?varchar(32)?CHARACTER?SET?utf8mb4?COLLATE?utf8mb4_general_ci?NULL?DEFAULT?NULL?COMMENT?'用戶唯一Id',`create_time`?datetime(0)?NULL?DEFAULT?NULL,`status`?int(1)?NULL?DEFAULT?NULL?COMMENT?'訂單狀態?1?未付款?2?已付款?3?已完成',PRIMARY?KEY?(`id`)?USING?BTREE )?ENGINE?=?InnoDB?AUTO_INCREMENT?=?7?CHARACTER?SET?=?utf8mb4?COLLATE?=?utf8mb4_general_ci?ROW_FORMAT?=?Compact;
    • undo_log:回滾日志表,同倉儲服務

    配置和seata相關配置

    Seata相關配置和倉儲服務相同,只不過需要在nacos中添加一個service.vgroupMapping.seata-order-tx-group=default,如下圖:

    扣減庫存的接口

    這里需要通過openFeign調用倉儲服務的接口進行扣減庫存,接口如下:

    以上只是簡單的通過openFeign調用,更細致的配置,比如降級,自己完善.........

    扣減余額的接口

    這里仍然是通過openFeign調用賬戶服務的接口進行扣減余額,接口如下:

    創建訂單的接口

    下訂單的接口就是一個事務發起方,作為TM,需要發起一個全局事務,詳細代碼如下圖:

    有什么不同?不同之處就是使用了@GlobalTransactional而不是@Transactional。

    @GlobalTransactional是Seata提供的,用于開啟才能全局事務,只在TM中標注即可生效。

    測試

    分別啟動seata-account9021、seata-storage9020、seata-order9022,如下圖:

    下面調用下單接口,如下圖:

    從控制臺輸出的日志可以看出,流程未出現任何異常,事務已經提交,如下圖:

    果然,查看訂單、余額、庫存表,數據也都是正確的。

    但是,這僅僅是流程沒問題,并不能說明分布式事務已經配置成功了,因此需要手動造個異常。

    在扣減余額的接口睡眠2秒鐘,因為openFeign的超時時間默認是1秒,這樣肯定是超時異常了,如下圖:

    此時,調用創建訂單的接口,控制臺日志輸出如下圖:

    發現在扣減余額處理中超時了,導致了異常.......

    此時,看下庫存的數據有沒有扣減,很高興,庫存沒有扣減成功,說明事務已經回滾了,分布式事務成功了。

    總結

    Seata客戶端創建很簡單,需要注意以下幾點內容:

    • seata客戶端的版本需要和服務端保持一致

    • 每個服務的數據庫都要創建一個undo_log回滾日志表

    • 客戶端指定的事務分組名稱要和Nacos相同,比如service.vgroupMapping.seata-account-tx-group=default

      • 前綴:service.vgroupMapping.

      • 后綴:{自定義}

    項目源碼已經上傳,關注公眾號碼猿技術專欄回復關鍵詞9528獲取!

    AT模式原理分析

    _____________________________________________________________________________

    AT模式最大的優點就是對業務代碼無侵入,一切都像在寫單體業務邏輯一樣。

    TC相關的三張表:

    • global_table:全局事務表,每當有一個全局事務發起后,就會在該表中記錄全局事務的ID

    • branch_table:分支事務表,記錄每一個分支事務的ID,分支事務操作的哪個數據庫等信息

    • lock_table:全局鎖

    一階段步驟

  • TM:seata-order.create()方法執行時,由于該方法具有@GlobalTranscational標志,該TM會向TC發起全局事務,生成XID(全局鎖)

  • RM:StorageService.deduct():寫表,UNDO_LOG記錄回滾日志(Branch ID),通知TC操作結果

  • RM:AccountService.deduct():寫表,UNDO_LOG記錄回滾日志(Branch ID),通知TC操作結果

  • RM:OrderService.create():寫表,UNDO_LOG記錄回滾日志(Branch ID),通知TC操作結果

  • RM寫表的過程,Seata 會攔截業務SQL,首先解析 SQL 語義,在業務數據被更新前,將其保存成before image(前置鏡像),然后執行業務SQL,在業務數據更新之后,再將其保存成after image(后置鏡像),最后生成行鎖。以上操作全部在一個數據庫事務內完成,這樣保證了一階段操作的原子性。

    二階段步驟

    因為“業務 SQL”在一階段已經提交至數據庫, 所以 Seata 框架只需將一階段保存的快照數據和行鎖刪掉,完成數據清理即可。

    正常:TM執行成功,通知TC全局提交,TC此時通知所有的RM提交成功,刪除UNDO_LOG回滾日志

    異常:TM執行失敗,通知TC全局回滾,TC此時通知所有的RM進行回滾,根據UNDO_LOG反向操作,使用before image還原業務數據,刪除UNDO_LOG,但在還原前要首先要校驗臟寫,對比“數據庫當前業務數據”和 “after image”,如果兩份數據完全一致就說明沒有臟寫,可以還原業務數據,如果不一致就說明有臟寫,出現臟寫就需要轉人工處理。

    AT 模式的一階段、二階段提交和回滾均由 Seata 框架自動生成,用戶只需編寫業務 SQL,便能輕松接入分布式事務,AT 模式是一種對業務無任何侵入的分布式事務解決方案。

    總結

    以上是生活随笔為你收集整理的分布式事务原理及实战seata(转自微信公众号 终码一生 )的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 国产一区二区视频免费 | 亚洲av无码不卡 | 欧美一区中文字幕 | 国产无人区码熟妇毛片多 | 国产成人一区二区三区电影 | 欧洲精品视频在线 | 伊人影院在线视频 | 国产五十路 | 红桃视频成人在线 | 9i看片成人免费高清 | 色播五月激情五月 | 国产黄色视 | 亚州福利| 欧美在线日韩在线 | 午夜精品久久久久久久99老熟妇 | 国产一二区视频 | 正在播放日韩 | 正在播放一区二区 | 丝袜一区二区三区 | 国产精品一区二区三区久久 | 无码人妻丰满熟妇精品区 | 五月天激情视频在线观看 | 神马影院午夜伦理片 | 色综合综合色 | 青草伊人网 | av天天操 | 日本美女视频一区 | 日美毛片 | 日韩不卡视频一区二区 | 老女人网站 | av网站在线免费观看 | 国产富婆一级全黄大片 | 黄色三级免费 | jizz视频| 日韩黄色片子 | 一二三区精品 | 国产成人无码精品久久久久久 | 一区三区视频在线观看 | 欧美日韩在线观看一区 | 你懂的网站在线观看 | 亚洲日本天堂 | 手机看片福利一区 | 男人的天堂一区 | xxx性日本 | 亚洲四区| 成人网页| 狠狠噜噜 | av片大全 | 免费精品在线视频 | 欧美激情亚洲综合 | 国产精品久久一区二区三区动 | 瑟瑟视频免费观看 | 亚洲一区二区免费在线观看 | 高跟鞋av | 毛片一级免费 | 97影音| 邻家有女4完整版电影观看 欧美偷拍另类 | 日韩在线不卡av | 91免费网站入口 | 国产一区,二区 | 精品国产无码一区二区 | 国产美女主播视频 | 99精品一区 | 日本成人在线不卡 | 日韩欧美成人一区 | aaaa毛片 | 日本wwwxx | 国产成人精品视频一区二区 | 日剧大尺度床戏做爰 | ww黄色 | 精品一区国产 | 亚洲天堂99| 五月婷婷伊人网 | 欧美在线视频免费观看 | 伊人久色 | 久久亚洲国产精品 | 五月六月婷婷 | 日韩精品在线观看视频 | 99久久精| 免费a级片在线观看 | 国产精品wwww | 性猛交娇小69hd | 一级大片免费观看 | 爽妇网av | 五月婷婷综合激情网 | 小向美奈子在线观看 | 免费一区二区视频 | 久久国产精品亚洲 | 国产理论在线观看 | 夜夜超碰 | 97自拍偷拍 | 一区二区国产在线观看 | 欧美日韩亚洲色图 | 欧美日韩人妻精品一区 | 欧美激情爱爱 | 久一在线视频 | 黄色一级带| 一区二区三区四区免费视频 | 老司机午夜免费福利 |