标签:参数 解决 分布 版本号 结果 body weight 订单 name
假如有个服务提供一个接口(服务部署在多个服务机器),接着有个接口是付款接口。用户在前端上操作的时候,一个订单不小心发起了两次支付请求,然后这两个请求分散在了这个服务部署的不同的机器上,结果一个订单扣款扣两次。这样的场景,就是接口没有保证幂等性的结果。
保证幂等性的核心
1.对于每个请求必须有一个唯一的标识。
2.每次处理完请求之后,必须有一个记录标识这个请求处理过了。
3.每次接收请求需要进行判断之前是否处理过的逻辑处理。
常见解决方案
1.业务表内唯一索引
如果要对创建销售出库单的接口保证幂等性,也就是说人家网络超时,重复调用的时候,保证一个订单只能有一个对应的销售出库单。针对销售出库单的表的订单id,创建一个唯一索引,你如果接口被重试,同一个订单创建一个销售出库单的话,一定会违反唯一索引,那么此时会报错。
2.业务表内状态机
修改订单状态,比如说将订单状态修改为【待发货】的时候,订单的状态其实就变为了【待发货】。
update order set status = ‘待发货‘ where status = ‘待付款‘ and id = 1;
这时候如果id为1的订单接口被重复调用了,即使再执行一次这个操作也不会有效果,因为这时候该订单记录的状态字段已经改变了,SQL并不会命中该记录。
在这种业务场景中也是通常都会有逻辑判断的,比如当前是否处于某个状态,然后才能流转到下一个状态(状态为待付款的才能流转到待发货)。
3.基于版本号的更新
id | name | age | version |
1 | yanggb | 18 | 1 |
给业务表内添加一个版本号的字段,如果要调用一个接口去更新年龄之前,就需要先查一下他的版本号是多少,然后调用接口的时候带上版本号。
在接口里保证分布式接口的幂等性(在更新的SQL中添加version的条件判断):
update user set age = 21, version = version + 1 where id = 1 and version = 1;
这样,多次提交的请求,因为版本号(version)都一样,因为第一次请求执行成功之后version已经+1了,则后面的请求因为version对应不上,都不会被执行。
4.基于MySQL的去重表 / 基于Redis的去重
比如说接口方法为changeAge(1, 21, 1),可以将所有的参数拼接成一个字符串,或者是从这些入参里选择一些参数(可以唯一标识这一次请求的一些参数),每次请求进来,在操作之前先校验这个字符串,如果校验通过则继续执行操作,校验失败则跳过。
如果基于MySQL,可以单独搞一个表出来(可以就一个字段),在这个表上的一个字段建一个唯一索引,插入的记录值就是前面拼接的字符串。因为第一次请求到达的时候,这个字符串在表中还不存在对应的记录,则会往表中插入该记录,并继续执行业务逻辑。后面的请求再到达的时候,因为这个字符串在表中已经存在了对应的记录了,唯一索引就会报一个冲突出来,这次插入就会失败,后续的业务逻辑也就会跳过执行了。
如果接口调用量很大,并发很高,还可以选择使用Redis。同样是拼接一个字符串串出来,直接set设置到Redis里去,如果下一次请求再过来,会发现这个key已经存在了,那么这个时候就不能执行了,因为已经可以知道出现了重复调用的情况了。
幂等的缺点
幂等是为了简化客户端逻辑处理,但是却增加了服务提供者的逻辑和成本,是否有必要在系统中引入幂等,需要根据具体场景具体分析,因此除了业务上的特殊要求外,一般建议是尽量不提供幂等的接口。
1.增加了额外控制幂等的业务逻辑,复杂化了业务功能。
2.把并行执行的功能改为串行执行,降低了执行效率。
"人不能太深情的活着,也不能总是追缅过去。"
标签:参数 解决 分布 版本号 结果 body weight 订单 name
原文地址:https://www.cnblogs.com/yanggb/p/11713309.html