如何处理接口幂等性问题

一.什么是幂等性?

在编程中一个幂等操作的特点是其任意多次执行所产生的影响与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变,例如,“setTrue”函数就是一个幂等函数,无论多次执行,其结果都是一样的,更复杂的幂等性是利用唯一交易号(流水号)实现。

二.什么是接口幂等性?

接口幂等性就是客户端对于同一操作发起的一次请求或者多次请求结果是一致的。例如,当用户购买商品后支付,支付扣款成功,但返回结果的时候网络异常,此时钱已经扣了,用户再次点击支付,第二次扣款,返回结果成功,用户查询余额发现扣多钱了,流水记录也变成了两条,这些就没有保证接口幂等性。任意多次执行所产生的影响均与一次执行的影戏相同,这是幂等性的特点,其实在我们的编程中主要操作的就是CURD,其中读(Retrieve)操作和删除(Detele)操作是天然幂等性的,受影响的是创建(Create)、和更新(Update)。

三.常见场景

1.前端重复提交

上面提到的支付场景,用户多次支付,还有用户注册、创建商品等等,如果用户不小心点击了多次,接口如果没有做幂等性的话,数据库中就会重复创建多条记录,这就是接口没有幂等新带来的bug。

2.接口超时失败

对于给第三方调用的接口,为了防止网络抖动或其他原因造成请求丢失,一般在设计的时候会对接口调用加上失败重试的机制。如果第一次调用已经执行了一半时,发生了网络异常。这时再次调用时就会因为脏数据的存在而出现调用异常。

3.消息重复消费

在使用消息中间件来处理消息队列,且手动 ack 确认消息被正常消费时。如果消费者突然断开连接,那么已经执行了一半的消息会重新放回队列。当消息被其他消费者重新消费时,如果没有幂等性,就会导致消息重复消费时结果异常,如数据库重复数据,数据库数据冲突,资源重复等。

四.接口幂等解决方案

一、Token机制

如何处理接口幂等性问题_第1张图片

1、服务端提供了发送Token的接口。我们在分析业务的时候,哪些业务是存在幂等问题的,就必须在执行业务前,先去获取Token,服务器会把Token保存到redis中。
2、然后调用业务接口请求时,把token携带过去,一般放在请求头部。
3、服务器判断token是否存在redis中,存在表示第一次请求,然后删除token,继续执行业务。(注意:redis要用删除操作来判断token,删除成功代表token校验通过,如果用select+delete来校验token,存在并发问题,不建议使用)
4、使用lua脚本原子验证令牌和删除令牌机制,否则还会有漏洞。如果快速点击了两下提交按钮,这时两个请求几乎同时进来,第一个先去查询redis,然后再准备删除token的时候,第二个也查了redis,存在Token,这样还是会造成重复提交。如果使用原子验证令牌,就可以完美解决该问题。

二、基于 mysql 实现

这种实现方式是利用 mysql 唯一索引的特性。
如何处理接口幂等性问题_第2张图片
1.建立一张去重表,其中某个字段需要建立唯一索引
2.客户端去请求服务端,服务端会将这次请求的一些信息插入这张去重表中
3.因为表中某个字段带有唯一索引,如果插入成功,证明表中没有这次请求的信息,则执行后续的业务逻辑
4.如果插入失败,则代表已经执行过当前请求,直接返回

三、基于 redis 实现

这种实现方式是基于 SETNX 命令实现的 SETNX key value:将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作。该命令在设置成功时返回 1,设置失败时返回 0。
如何处理接口幂等性问题_第3张图片
1.客户端先请求服务端,会拿到一个能代表这次请求业务的唯一字段
2.将该字段以 SETNX 的方式存入 redis 中,并根据业务设置相应的超时时间
3.如果设置成功,证明这是第一次请求,则执行后续的业务逻辑
4.如果设置失败,则代表已经执行过当前请求,直接返回

你可能感兴趣的:(如何处理接口幂等性问题)