业务及接口幂等性设计
背景
实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返回一样的结果。例如:
1.、前端没有控制住用户操作,导致重复提交选中的数据,后台应该只产生对应这个数据的一个反应结果;
2、用户发起一笔付款请求,应该只扣用户账户一次钱,当遇到网络重发或系统 bug 重发,也应该只扣一次钱;
3.、发送短信,发多了用户烦,而且量大了成本也高;
4、创建业务订单,一次业务请求只能创建一个,创建多个就会出大问题等等很多重要的情况都需要幂等的特性来支持。
幂等
幂等就是指一个操作不论执行多少次,产生的效果和返回的结果都是一样的
设计
1、查询、删除天然幂等
2、数据库唯一索引保证新增幂等,比如用户额度表,一个用户在一家资方只能存在一条额度记录,则可以建立唯一索引UNIQUE KEY unq_uid_cid
(user_id
,company_id
)
3、乐观锁:版本号或者条件限制。比如如下资方额度扣减,可以保证高并发情况下不会出现额度超扣。
update table set avail_amount=avail_amount - #{subAmount} where avail_amount - #{subAmount} >= 0
4、分布式锁:比如用户点击下单情况,同一个时刻需要保证用户只能下一单。
订单单号生成设计:精确到毫秒的时间+业务编码+用户ID+三位随机数
20200114095734531 001 777777 123
获取REDIS分布式锁步骤:SETNX的key为用户ID+资方ID,value为随机生成的一个UUID并设置过期时间,做好下单业务后,FINALLY由同一个线程释放锁。具体参看我的分布式锁文章。
5、状态机幂等:
交易状态基本可以分为这几种:处理中、成功、失败、未知,如有需要可以在加入初始化等等状态。终态比如成功、失败的就不可再进行处理,而其他的则看业务需求发起查询或者重试。代付、代扣、还款等等都可以使用这套枚举。
6、API接口幂等:
我理解是那种以一个请求编号多次调用(通过定时任务)的接口才需要解决幂等性问题,比如支付宝代付回调、同一笔订单的打款回调、同一笔账单还款回调;而如果是每次都生成新的请求编号,那么幂等性就没有作用了,纯粹只能记录下每次请求记录。
解决:数据库新增一张幂等表,将唯一请求编号作为唯一索引,记录请求处理状态。
请求进来时先根据请求编号查询是否存在这条记录
1、不存在:插入并将请求处理状态设为处理中并进行后续处理,返回处理状态
2、存在且状态为成功:直接返回调用方处理成功,调用方停止调用
3、存在且状态为处理中:直接返回调用方处理中
4、存在且状态为失败:这种情况多半是业务系统或请求数据出现异常,需要人工处理,可以返回调用方请求失败,调用方停止调用,双方共同介入排查。这种情况要设置一下报警。且返回状态需要看调用方约定
7、token机制,每次接口请求前先获取一个token,然后再下次请求的时候在请求的header体中加上这个token,后台进行验证,如果验证通过删除token,下次请求再次判断token。可以借助JWT
总结
幂等性是系统设计时需要密切考虑的问题,特别是涉及钱的银行、信托或互联网金融公司等,即要保证接口高效,但更要考虑数据准确性问题,一旦代付、代扣、还款出问题,影响的就是公司的信誉和技术问题了。