幂等接口的设计

什么是幂等接口?

就是不管调用几次这个接口,最终结果是一样的。像查询就是天然的幂等接口,http中的get,put,delete也是。

再抛出一道笔者实际遇到的面试题,如果第三方支付接口调用超时,该怎么做,考虑有两种情况

1.调用支付接口的时候就超时了,
2.调用接口完成,数据已经加入到数据库中了,这个时候返回结果的时候可能因为网络波动、sql语句执行时间过长等原因超时
注意,我们并不知道是哪一步失败了,
接口不是幂等的:我们就需要先去查表中数据是否被修改,如果查询为支付成功,直接返回成功,如果是待支付,那么再调用一次接口。
比如支付宝的扣款,应当有一张流水表来记录每一笔款项,流水表中还会有支付状态的字段,而查询应当带有全局唯一的订单流水号去查这个支付状态
接口是幂等:返回超时,我们只需要再次调用这个幂等接口就行了。

那么如何设计幂等接口?

1.全局唯一ID
在执行操作前先根据这个全局唯一ID是否存在,来判断这个操作是否已经执行。如果不存在则把全局ID,存储到存储系统中,比如数据库、redis等。如果存在则表示该方法已经执行。
使用全局唯一ID是一个通用方案,可以支持插入、更新、删除业务操作。
2.Insert场景的方案
在以上的支付场景中,如果一个订单只会支付一次,所以订单ID可以作为唯一标识。这时,我们就可以建一张去重表,并且把唯一标识作为唯一索引,在我们实现时,把创建支付单据和写入去去重表,放在一个事务中,如果重复创建,数据库会抛出唯一约束异常,操作就会回滚。
在秒杀场景中,还可以在订单表中商品id和用户id作为联合唯一索引,可以做到一个人只能购买一件商品。
3.Update场景的方案
比如我们要更新商品的名字,这时我们就可以在更新的接口中增加一个版本号,来做幂等
方法实现如下

boolean updateGoodsName(int id,String newName,int version);

SQL语句如下

update goods set name=#{newName},version=#{version} where id=#{id} and version<#{version}

(这个sql语句没用代码块的格式是因为markdown语法不熟,放在代码块里会出问题,见谅)

4.表中的状态字段
比如就会订单的创建和付款,订单的付款肯定是在之前,这时我们可以通过在设计状态字段时,使用int类型,并且通过值类型的大小来做幂等,比如订单的创建为0,付款成功为100。付款失败为99,是不是与最上面提到的非幂等接口的情况很像。
幂等接口的sql语句如下:
update order set status=#{status} where id=#{id} and status<#{status}
非幂等接口的sql语句如下:
update order set status=#{status} where id=#{id}
可以看到非幂等接口少了个判断,只要调用就会去更新,如果要正常使用需要我们在业务中再次查询,而幂等接口就可以随意调用。

你可能感兴趣的:(幂等接口的设计)