对接一个第三方服务接口要考虑什么?(一)

通信标准

  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层处理一致性问题。最后留下了一个问题还未进行讨论,在入库前、从新发起请求前、异步通知时都须要考虑幂等的问题,下一篇文章针对幂等再来分享几种处理方案吧。

相关文章
相关标签/搜索