githubjava
客戶端調用A服務的接口,A服務接口中又調用了B服務。 若是A服務和B服務都執行成功,則成功,而且兩者事務都應提交; 若是A服務或B服務任意一個失敗,則失敗,且兩者事務都不執行或回滾。 由於網絡請求的不可靠性,若是A調用B失敗,可能:1. B沒有接收到網絡請求;2. B收到後執行失敗; 3. B執行成功,請求返回時異常。 4. B調用超時,B可能執行成功也可能失敗。所以當A調用B時,可能出現不一致。git
A服務接口:github
try {
業務代碼A
feign調用B服務接口
事務提交
} catch (Exception e) {
事務回滾
}
複製代碼
B服務接口:bash
try {
業務代碼B
事務提交
//此時接口狀態碼2XX
} catch (Exception e) {
事務回滾
//此時接口狀態碼非2XX
}
複製代碼
一致性分析:網絡
服務A業務代碼執行完後發送消息到消息隊列,如rabbitmq,並用ack等方式確保發送成功; B服務接收消費成功後,手動確認消息,kafka則用手動提交位移的方式。異步
A服務生產者:優化
try {
業務代碼A
ack = rabbitmqProducer.sendAndGetAck
if !ack {
//發送失敗能夠設置自動重試,不重試就拋出異常
throws new RabbitmqSendException
}
事務提交
} catch (Exception e) {
//事務回滾
}
複製代碼
B服務消息隊列消費端一:ui
//
try {
業務代碼B
channel.basicAck手動確認//kafka則手動提交位移
事務提交
//此時接口狀態碼2XX
} catch (Exception e) {
事務回滾
//此時接口狀態碼非2XX
}
複製代碼
B服務消息隊列消費端二:spa
//
try {
業務代碼B
事務提交
//此時接口狀態碼2XX
} catch (Exception e) {
事務回滾
//此時接口狀態碼非2XX
}
channel.basicAck手動確認//kafka則手動提交位移
複製代碼
一致性分析:code
A服務須要插入一個表transaction_record記錄調用狀態,提供給B服務回調。
A服務業務接口:
String uuid = generateUUID()//生成一個uuid
try{
feign.preCreate(uuid,...)//feign調用B預執行,好比B服務爲建立訂單服務,預建立一個訂單,但狀態爲待確認
業務代碼A
將uuid插入transaction_record表中
事務提交
}catch (Exception e) {
事務回滾
feign.cancel(uuid)//feign調用B取消,好比B服務爲建立訂單服務,設置該訂單狀態爲取消
}
feign.confirm(uuid)//feign調用B確認,好比B服務爲建立訂單服務,設置該訂單狀態爲確認,此時訂單可用
複製代碼
A服務服務回查接口,提供給B服務回查狀態:
get /v1/transaction/{uuid}
從transaction_record表中查詢,有則返回確認,沒有則返回取消
複製代碼
B服務須要提供預執行、確認、取消接口。若預執行後遲遲沒有執行確認或取消,則B向A回查,根據結果確認或取消。
一致性分析:
以上是接口間調用的幾種方式,這裏只提供一種大概的思路,應用時能夠本身優化,同步發送也可修改成異步+重試等方式。 若一致性要求可採用方式三,uuid+插入表也可採用其餘的方式實現。爲了提升一致性,接口調用也要儘可能是冪等的,可經過業務邏輯的冪等性或 uuid實現。