09-服务幂等
服务幂等
幂等的场景
- 前端重复提交:提交订单,用户快速重复点击多次,造成后端生成多个内容重复的订单。
- 接口超时重试:对于给第三方调用的接口,为了防止网络抖动或其他原因造成请求丢失,这样的接口一般都会设计成超时重试多次。
- 消息重复消费:MQ消息中间件,消息重复消费。
幂等的实现方式
Token机制
类似于前端重复提交、重复下单的场景就可以通过 Token 的机制实现
针对前端重复连续多次点击的情况,例如用户购物提交订单,提交订单的接口就可以通过 Token 的机制实现防止重复提交。
服务端提供了发送token的接口。我们在分析业务的时候,哪些业务是存在幂等问题的,就必须在执行业务前,先去获取token,服务器会把token保存到redis中。(微服务肯定是分布式了,如果单机就适用jvm缓存)。
然后调用业务接口请求时,把token携带过去,一般放在请求头部。
服务器判断token是否存在redis中,存在表示第一次请求,这时把redis中的token删除,继续执行业务。
如果判断token不存在redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码,不被重复执行。
乐观锁
- 增加版本号字段:在涉及更新的数据表中增加一个 version 字段,更新数据时,版本号随之增加。
- 更新时检查版本号:执行更新操作时,通过 WHERE 子句检查当前版本号是否与读取时的版本号一致,如果一致则执行更新,并更新版本号。
- 重试机制:如果更新操作失败,意味着数据库内的数据已经被修改。此时,业务层面请求最新的数据,更新本地 version 并发起重试,直至成功或达到最大重试次数
数据库去重表
- 那些重复消费和接口重试的场景则使用数据库唯一索引的方式实现更合理
- 往去重表里插入数据的时候,利用数据库的唯一索引特性,保证唯一的逻辑。唯一序列号可以是一个字段,例如订单的订单号,也可以是多字段的唯一性组合
- 自定义一个注解,在需要幂等性保证的接口上加上该注解,然后通过拦截器方法拦截使用。这样简单便不会造成代码侵入和污染。
- 缺点
- 系统容错性不高,如果幂等表所在的数据库连接异常或所在的服务器异常,则会导致整个系统幂等性校验出问题。如果做数据库备份来防止这种情况,又需要额外忙碌一通了啊。
Redis实现
Redis实现的方式就是将唯一序列号作为Key,唯一序列号的生成方式和上面介绍的防重表的一样,value可以是你想填的任何信息。唯一序列号也可以是一个字段,例如订单的订单号,也可以是多字段的唯一性组合。当然这里需要设置一个 key 的过期时间,否则 Redis 中会存在过多的 key。
状态机
- 对于很多业务是有一个业务流转状态的,每个状态都有前置状态和后置状态,以及最后的结束状态。例如流程的待审批,审批中,驳回,重新发起,审批通过,审批拒绝。订单的待提交,待支付,已支付,取消。
以订单为例,已支付的状态的前置状态只能是待支付,而取消状态的前置状态只能是待支付,通过这种状态机的流转我们就可以控制请求的幂等。 - 假设当前状态是已支付,这时候如果支付接口又接收到了支付请求,则会抛异常或拒绝此次处理。
09-服务幂等
https://x-leonidas.github.io/2022/02/01/12分布式系统/09-服务幂等/