對接一個第三方服務接口要考慮什麼?(一)

通信標準

  1. 肯定協議 Http/Https
  2. 肯定參數傳輸格式 json/x-www-form-urlencoded/...
  3. 具體接口參數的數據類型(弱類型對接強類型語言時容易踩坑)
  4. 肯定身份校驗方式
    • token
    • 參數簽名:單一Key、交換公鑰
    • 證書校驗
  5. 肯定響應結構(統一成功響應碼等等)

異常處理

通常狀況下,異常結構應該區分爲三大類:java

  1. RpcException:調用異常
  2. BusinessException:業務異常
  3. Exception:其餘程序異常

對於不一樣的第三方服務,什麼狀況下對應什麼異常,可能會有不一樣的劃分標準,通常狀況下有如下規則:數據庫

  1. Http StatusCode == 500 時爲RpcException
  2. 業務響應碼 != 統一成功響應碼 時爲BusinessException
  3. 其餘異常不做歸類

一致性

對於涉及到支付、退款等有下單概念的接口或涉及到狀態問題時,則須要考慮到一致性的問題。通常狀況下有如下要求(第三方服務也叫上游):json

  1. 上游有的數據,本地必定要有
  2. 上游狀態與本地每條數據的狀態相同

因爲第三方服務通常不受控,這裏說的一致性每每只能是最終一致性異步

事務發起

  1. 肯定接口當中的惟一標識是哪一個字段,一般是requestNo,這個字段的值將上游數據和本地數據進行一一對應,上游存在的requestNo,本地必須存在url

  2. 劃分狀態:spa

  • PENDING:本地數據已建立,未發起接口請求
  • UNCONFIRMED:本地數據已建立,不知道接口請求發起了沒有,等待回查
  • PROCESSING:接口請求已發起,而且上游已響應,等待回查確認最終狀態
  • SUCCESS:終態,業務已成功
  • FAIL:終態,業務已失敗
  • DEAD:終態,本地數據已建立,接口死活請求不了,上游也查不到對應數據,不要了,根據實際狀況也能夠歸類爲FAIL

這裏的流程能夠概述爲:code

  1. 發起上游接口前,生成一個全局惟一的requestNo,該條數據的狀態爲PENDING,而且入庫提交。
  2. 請求上游接口沒有異常的狀況下:
    1. 若是容許的話,同步處理狀態,更新狀態入庫。
    2. 不然,直接更新爲PROCESSING,表示請求上游已成功,等待進一步確認狀態。
  3. 請求上游接口遇到RpcException,更新爲PROCESSING,表示請求上游已成功,等待進一步確認狀態。
  4. 請求上游接口遇到BusinessException,更新爲FAIL,表示請求上游已成功,等待進一步確認狀態。(這裏可能根據不一樣的業務返回碼,處理爲PROCESSING,等待進一步確認)
  5. 處理過程當中遇到其餘Exception,更新爲UNCONFIRMED,不能肯定是否已請求上游,等待進一步確認狀態,這裏能夠概述爲本地事務處理失敗,即保存到本地數據庫時失敗。

若是你對上游的信任度較低,能夠直接將PROCESSING狀態也合併爲UNCONFIRMED通一由事務回查處理orm

下面用一段僞代碼來描述接口調用的流程:索引

// 開啓本地事務
startTrans();
Order order = new Order();
// 惟一請求號
String requestNo = UUID();
order.setRequestNo(requestNo);
order.setState(OrderState.PENDING);
order.save();
 // 提交本地事務
commit();

try {
    startTrans();
    RpcResponse rpcRes = rpcService.requestToRpcCreateOrder(...);
    order.setState(OrderState.PROCESSING); 
    
    // 若是你的接口能夠同步返回業務狀態
    if(rpcRes.getState() == 'SUCCESS') {
        order.setState(OrderState.SUCCESS); 
    }
    if(rpcRes.getState() == 'FAIL') {
        order.setState(OrderState.FAIL); 
    }
    
    order.save();
    commit();
} catch(RpcException e) {
    startTrans();
    // 認爲是處理中,等待後續回查
    order.setState(OrderState.PROCESSING);
    order.save();
    commit();
} catch(BusinessException e) {
    startTrans();
    // 認爲是失敗
    order.setState(OrderState.FAIL);
    // 失敗時,建議記錄rpc響應參數
    order.setRpcResponseCode(e.getCode());
    order.setRpcResponseMsg(e.getMsg());
    order.save();
    commit();
} catch(Exception e) {
    startTrans();
    // 認爲是待確認,等待後續回查
    order.setState(OrderState.UNCONFIRMED);
    order.save();
    commit();
}
複製代碼

事務回查(重試)

通過上面的流程,數據會剩下UNCONFIRMED 和 PROCESSING 兩種狀態,所以對這兩種狀態進行進一步確認,保證數據到達終態。token

事務回查有幾種實現方式:

  1. 利用定時器掃描數據庫狀態爲UNCONFIRMED或PROCESSING的數據
    • 保證數據庫有索引
    • 若是requestNo字段也有索引,則可利用覆蓋索引機制縮短查詢時間,查詢上游數據狀態通常只須要requestNo
  2. 把UNCONFIRMED或PROCESSING數據的requestNo存入Redis,再利用定時器處理
  3. 利用隊列,將UNCONFIRMED 和 PROCESSING塞在回查隊列中

實際上,假如你的rpc請求不需同步返回出去,推薦使用具備事務機制的消息隊列,不然利用隊列方案須要考慮複雜度的上升程度

那麼UNCONFIRMED 和 PROCESSING分別怎麼處理呢

  • 對應PROCESSING,處理思路很簡單,由於這種狀態上游確定可以返回對應的狀態(實際上有的上游並不必定),只要查詢到對應狀態更新爲SUCCESS或FAIL便可

  • 對應UNCONFIRMED須要區分上游數據不存在的狀況,也就是說上面的事務發起流程當中,上游沒有收到咱們的請求,那麼咱們須要根據業務狀況進行處理:

    • 從新發起這個請求(要確保上游接口是否冪等,不然要本身處理)
    • 更新爲DEAD,拋棄這個請求

    若是上游存在該記錄,則視爲PROCESSING狀況處理便可

若是你的上游提供異步處理通知,則可按照一樣的思路完成事務回查這個階段

總結與思考

本文簡單總結了一下對接第三方服務接口時須要考慮的幾個問題:通信標準、異常處理、一致性,實際處理時一般會分爲RPC層與Service層來處理,RPC封裝通信標準、異常處理的問題,Service層處理一致性問題。最後留下了一個問題還未進行討論,在入庫前、從新發起請求前、異步通知時都須要考慮冪等的問題,下一篇文章針對冪等再來分享幾種處理方案吧。

相關文章
相關標籤/搜索