目录
幂等性在我们的实际开发中是比较常见的,例如转账提现、订单发货系统等。今天,我们就分这两种场景对幂等性进行讨论:
1.转账提现
假如让我们实现支付宝和银行的转账提现需求,你会怎么做呢?
首先我们看一下示意图:
用户登录支付宝发起提现请求,支付宝调用银行的出款接口,再将相关记录落入数据库。正常情况下,这个流程没有问题,可是如果有一天银行的出款接口超级慢,或者干脆挂掉了,应该如何处理呢?我们如果直接提示,“出款失败,请明天再来或者稍后再试”这样的字样显示是非常不友好的,会带来用户的损失。正确的做法应该是?
- 假如是因为接口超时导致无法出款的话,我们可以做一次接口重试。当然,银行的接口必须满足幂等性,防止重复提交导致重复扣款。此时的幂等性可以使用一个提交的版本号,银行系统遇到和之前处理请求的版本号相同或者小的版本号,就不做处理。
- 银行系统接口假如没有实现幂等性呢?这在现实中是非常常见的,此时我们可以给用户返回成功,保存用户的请求,后面定时提交。
2.发货系统
电商行业的发货系统通常是这样的,用户提交订单,订单系统调用发货接口开始发货,并将相关记录落库。示意图如下:
发货系统提供的发货接口为:
- 接口地址:http://127.0.0.1:9080/trans/sendOrder?order=1
- 接口参数:order=1 订单号为1
- 接口方法:对订单1进行发货
当系统页面很卡顿的时候,用户在页面连续点击了多次相同的请求,会出现什么结果呢?如果不做幂等性处理,有可能多次调用发货系统,导致一个订单发货多次,此时我们可以使用版本号的方式解决该问题。首先新建一张订单表:
CREATE TABLE `order` (
`orderid` VARCHAR(32) DEFAULT NULL,
`ordertime` DATETIME DEFAULT NULL,
`ordermoney` DECIMAL(20,0) DEFAULT NULL,
`orderstatus` CHAR(1) DEFAULT NULL,
`version` INT(11) DEFAULT NULL
) ENGINE=INNODB DEFAULT CHARSET=utf8;
利用数据库的事务的原子性,当对该表有更新操作的时候,同时更新版本号。例如存在如下未发货订单记录:
('1','2017-12-06 11:36:23','12','1',0)
1表示已经付款,2表示发货中。当订单执行发货的时候执行如下SQL:
update order set orderstatus=2,version=version+1 where orderstatus = 1 and version = 0
这样通过引入版本号,就可以避免重复发货的问题。