幂等,指的是任意多次执行所产生的影响均与一次执行的影响相同
幂等方法,可以是相同参数重复执行,并能获得相同结果的方法,这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变
7.1.1 业务 CRUD
其中的读取 Retrieve 和删除 Delete 是天然幂等的,受影响的就是创建 Create 和更新 Update
7.1.2 HTTP 协议
HTTP 中有各种各样的请求方法,其中幂等的方法有
GET
HEAD
PUT
DELETE
非幂等方法有:
POST
7.2、幂等性应用场景
接口的幂等性设计,也有很多业务的应用场景
业务中考虑幂等性的地方一般是接口的重复请求,会有以下几种常见场景导致接口重复被请求:
前端重复提交:提交订单,用户快速重复点击多次,造成后端生成多个内容重复的订单
接口超时重试:对于第三方调用接口,为了防止网络抖动或其他原因导致丢失,接口一般会设计成超时重试多次
消息重复消费:防止 MQ 中的消息被重复消费
金融交易业务:防止支付请求可能被重复发送
7.3、接口幂等性实现方式
7.3.1 前端简单拦截
控制前端的组件可见性来拦截重复提交
防止表单重复提交、不可点击的方案(按钮置灰、隐藏按钮)
7.3.2 Token 机制
利用 Token + Redis 验证
来防止重复提交
Client 没有 token,请求 Server,获取 token
Server 生成 token,并缓存到 Redis 中,返回 token 给 Client
Client 带着 Token(放到报文头),请求 Server 进行业务操作
Server 首先验证 Token,在 Redis 查询该 Token 是否存在
Redis 有 Token,说明是第一次提交,执行业务,并删除 Token
Redis 没 Token,说明是重复提交,不执行业务,返回拒绝请求
可以看到,生成的每一次 Token 的有效期都是一次业务未执行到执行完期间,每一次业务操作对应着一个唯一的 Token
7.3.3 Redis 缓存唯一序列号
比起 Token,可以直接把业务相关的信息缓存到 Redis 中,报文头部不需要存东西,对于客户端来说更轻量级
例如,一次支付请求可以这么做:
Server 首先验证进来的支付请求,检查缓存是否有订单 ID
Redis 里面没有订单 ID,缓存支付请求的订单 ID ,然后去做相应的业务操作
无论支付操作成功还是失败,在支付操作结果返回后,在 Redis 里删除该订单 ID 的 Key
Redis 里面有订单 ID,直接返回重复请求的提示结果,支付终止
所以,基于 Redis 来说,可以这么存储:
Key:唯一序列号,生成唯一的 Key 可以借助雪花算法和自定义的字段组合来生成
Value:任何想填的信息,比如订单的相关信息等
例如,对于 KafKa 的重复消费,也可以使用这样的方式来做,那之前,先看看 Kafka 的重复消费是指的什么
KafKa 的消费过程是这样的
生产者生产一个消息,给 Kafka
Kafka 格式化生产消息,也就是每个消息会有一个 offset,把数据格式化成 msg | offset 这样的形式
消费者与 ZK 协调,ZK 记录消费者当前消费到的消息的 offset,消费者会从 ZK 指定的 offset 顺序消费
消费者会定期提交一次当前消费的 offset,而不是消费一条就提交一次