幂等性
幂等,这个词来源自数学领域。幂等性衍生到软件工程中,它的语义是指:函数/接口可以使用相同的参数重复执行, 不应该影响系统状态,也不会对系统造成改变。
举一个简单的例子:正常设计的查询接口,不管调用多少次,都不会破坏当前的系统或数据,这就是一个幂等操作。
由于分布式天然特性的时序问题以及网络的不可靠性(机器、机架、机房故障、电缆被挖断等等), 重复请求很常见,接口幂等性设计就显得尤为重要。
幂等需要考虑的场景有很多,例如系统A是处理用户客户端发送过来的请求,无论是前端bug、脚本恶意发包、用户重复点击又或是网络超时导致的网络重发,都会造成系统A收到相同参数的网络请求。
保障幂等性的原理
对于分布式系统来说,在JVM层面的锁已经失去作用,所以保证系统幂等性需要满足3个条件:
- 请求唯一标识:每一个请求必须有一个唯一标识。
- 处理唯一标识:每次处理完请求之后,必须有一个记录标识这个请求处理过了。
- 逻辑判断处理:每次接收请求需要进行判断之前是否处理过的逻辑处理。根据请求唯一标识查询是否存在处理唯一标识。
幂等性实现方案
1. token机制
针对客户端重复连续多次点击的情况,例如用户购物提交订单,提交订单的接口就可以通过token机制实现防止重复提交。

主要流程就是:
- 服务端提供生成请求token的接口。在存在幂等问题的业务执行前,向服务器获取请求token,服务器会把token保存到Redis中。
- 然后调用业务接口请求时,把请求token携带过去,一般放在请求头部。
- 服务器判断请求token是否存在redis中:存在则表示第一次请求,这时把Redis中的token删除,继续执行业务;如果判断token不存在redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码,不被重复执行。
这里要结合业务考虑这种场景:如果请求处理失败,前端是否需要重新申请token进行重试(因为此时token在服务端已经被删除)。
2. 数据库唯一索引
往数据库表里插入数据的时候,利用数据库的唯一索引特性,保证唯一的逻辑。唯一序列号可以是一个字段,例如订单的订单号,也可以是多字段的唯一性组合。
事务中包含多表数据的更新,业务要考虑处理事务回滚的问题。
3. Redis实现
Redis实现的方式就是将唯一序列号作为Key存入Redis,在请求处理之前,先查看Key是否存在。唯一序列号可以是一个字段,例如订单的订单号,也可以是多字段的唯一性组合。当然这里需要设置一个key的过期时间,否则Redis中会存在过多的key。具体校验流程如下图所示:

如果想要基于Redis实现幂等性防重框架,需要考虑如下两个问题:
- 如果第一次请求失败了,客户端重试,是否需要放行?
- 网络请求可能是get或者post(内部rpc协议除外),唯一序列号参数可能在url或是在body体里。则使用防重框架的新接口以及之前老业务接口能否做到版本兼容性?
建议业务使用方最好针对指定业务进行Redis的幂等方案。
Zookeeper同样也能实现上述功能,但由于Zookeeper是CP模型,性能不如Redis,另外针对防重场景,也并不需要Zookeeper高可靠性,所以优先推荐Redis。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!