20200427 補償模式

微服務架構應滿足數據最終一致性,實現最終一致性的三種模式:可靠事件模式、補償模式、TCC模式。

 

事務補償機制

事務補償即在事務鏈中的任何一個正向事務操作,都必須存在一個完全符合回滾規則的可逆事務。如果是一個完整的事務鏈,則必須事務鏈中的每一個業務服務或操作都有對應的可逆服務。

實現真正的預提交和正式提交的分離。

需要手工編寫大量的代碼來處理以保證事務的完整性。

考慮實現一個通用的事務管理器,實現事務鏈和事務上下文的管理。對於事務鏈上的任何一個服務正向和逆向操作均在事務管理和協同器上註冊,由事務管理器接管所有的事務補償和回滾操作。

BASE策略中的基於消息的最終一致性是比較好的解決方案。

 

補償模式

第一部分:實現補償模式的關鍵在於業務流水的記錄;要實現補償過程,我們需要做到兩點

1、首先要確定失敗的步驟和狀態,從而確定需要補償的範圍。

2、其次要能提供補償操作使用到的業務數據。

關聯表,分爲框架表和業務表,技術表中保存爲實現補償操作所需要的技術數據,業務表保存業務數據,通過在技術表中增加業務表名和業務表主鍵來建立和業務數據的關聯。

 

第二部分:通過重試來保證補償過程的完整,從而滿足最終一致性。

補償過程作爲一個服務調用過程同樣存在調用不成功的情況,這個時候需要通過重試的機制來保證補償的成功率。當然這也就要求補償操作本身具備冪等性(重試就要保證冪等)。

 

不同的重試策略

如果只是一味的失敗就立即重試會給工作服務造成不必要的壓力,我們要根據服務執行失敗的原因來選擇不同的重試策略。(重試也有不同的策略)

1)如果失敗的原因不是暫時性的,由於業務因素導致(如業務要素檢查失敗)的業務錯誤,這類錯誤是不會重發就能自動恢復的,那麼應該立即終止重試。(參數錯誤)

2)如果錯誤的原因是一些罕見的異常,比如因爲網絡傳輸過程出現數據丟失或者錯誤,應該立即再次重試,因爲類似的錯誤一般很少會再次發生。(網絡抖動,在發生網絡抖動時可以進行自動地補償調用)

3)如果錯誤的原因是系統繁忙(比如http協議返回的500或者另外約定的返回碼)或者超時,這個時候需要等待一些時間再重試。(響應超時)

 

重試操作一般會指定重試次數上線,如果重試次數達到了上限就不再進行重試了。這個時候應該通過一種手段通知相關人員進行處理。

對於等待重試的策略如果重試時仍然錯誤,可逐漸增加等待的時間,直到達到一個上限後,以上限作爲等待時間。

如果某個時刻聚集了大量需要重試的操作,補償框架需要控制請求的流量,以防止對工作服務造成過大的壓力。

 

補償模式補充說明

1.微服務實現補償操作不是簡單的回退到業務發生時的狀態,因爲可能還有其他的併發的請求同時更改了狀態。一般都使用逆操作的方式完成補償。

2.補償過程不需要嚴格按照與業務發生的相反順序執行,可以依據工作服務的重用程度優先執行,甚至是可以併發的執行。

 

TCC模式是優化的補償模式。

1、所有事務參與方都需要實現try,confirm,cancle接口。

2、事務發起方向事務協調器發起事務請求,事務協調器調用所有事務參與者的try方法完成資源的預留,這時候並沒有真正執行業務,而是爲後面具體要執行的業務預留資源,這裏完成了一階段。

3、如果事務協調器發現有參與者的try方法預留資源時候發現資源不夠,則調用參與方的cancle方法回滾預留的資源,需要注意cancle方法需要實現業務冪等,因爲有可能調用失敗(比如網絡原因參與者接受到了請求,但是由於網絡原因事務協調器沒有接受到回執)會重試。

4、如果事務協調器發現所有參與者的try方法返回都OK,則事務協調器調用所有參與者的confirm方法,不做資源檢查,直接進行具體的業務操作。

5、如果協調器發現所有參與者的confirm方法都OK了,則分佈式事務結束。

6、如果協調器發現有些參與者的confirm方法失敗了,或者由於網絡原因沒有收到回執,則協調器會進行重試。這裏如果重試一定次數後還是失敗,會怎麼樣那?常見的是做事務補償。

 

與補償模式不同的是TCC服務框架不需要記錄詳細的業務流水,完成confirm和cancel操作的業務要素由業務服務提供。

發送消息之前就開始存儲這些信息。

接口補償還是kafka發送消息的補償,這兩者記錄的字段不一樣,對錶設計有影響。

協調服務爲一個通用的補償框架。

 

補償模式表設計
CREATE TABLE `data_send_status` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`flow_id` int(11) NOT NULL COMMENT '對應業務id',
`service_type` tinyint(4) NOT NULL COMMENT '1表示支付服務調用訂單服務',
`send_topic` varchar(255) DEFAULT NULL COMMENT '發送對應topic',
`send_params` varchar(255) DEFAULT NULL COMMENT '參數',
`error_msg` varchar(255) DEFAULT NULL COMMENT '錯誤信息',
`is_prepare` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0表示準備發送,1表示發送結束',
`is_success` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0表示發送失敗,1表示發送成功',
`delete_flag` int(1) NOT NULL COMMENT '刪除標誌',
`created_id` int(11) NOT NULL COMMENT '創建人id',
`created_time` datetime NOT NULL COMMENT '創建時間',
`modified_id` int(11) NOT NULL COMMENT '修改人id',
`modified_time` datetime NOT NULL COMMENT '修改時間',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='數據發送狀態記錄表';

 

CREATE TABLE `data_auto_compensation` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_send_id` int(11) NOT NULL COMMENT '數據發送失敗的記錄id',
`retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '重試次數',
`max_retry_count` int(11) NOT NULL DEFAULT '3' COMMENT '最大重試次數',
`status` int(11) NOT NULL COMMENT '狀態 0未解決 1已解決',
`delete_flag` int(1) NOT NULL COMMENT '刪除標誌',
`created_id` int(11) NOT NULL COMMENT '創建人id',
`created_time` datetime NOT NULL COMMENT '創建時間',
`modified_id` int(11) NOT NULL COMMENT '修改人id',
`modified_time` datetime NOT NULL COMMENT '修改時間',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='數據補償表';

 

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