25.9.16幂等性总结
幂等接口与防重提接口
首先幂等性的概念很容易被误解,最初了解幂等性是通过一个例子:重复按提交按钮都会返回‘已提交’,而不会在数据库中接收到多条提交信息。这里是设计了幂等性的,但是如果是拒绝相同请求,返回的是‘已提交,请勿重复提交’这里不是幂等性设计而是防重提设计。这里需要做一个区分。
幂等问题出现情况与原因
概念上分为请求幂等与事务幂等,后端软件开发主要遇到的是事务幂等。
所谓请求幂等其实就是http请求执行多次,结果不要改变,而请求分为以下几种:
天然幂等的方法 (Safe Methods):GET, HEAD, OPTIONS
设计上幂等的方法 (Idempotent but Not Safe)PUT, DELETE
这些方法会改变服务器状态,但它们被设计成幂等的,最终即使多次请求状态也不会改变。
天然不幂等的方法 (Non-idempotent)POST, PATCH
这两个请求如果多次执行会导致状态改变,所以在事务上我们需要设计幂等的接口,保证幂等性,这就来到了事务幂等性。
事务幂等性问题主要由网络延迟(客户端没收到请求消息重发)、RPC远程调用重试机制、分布式消息中间件投递策略(一条消息可能被投递多次)、人为重复点击按钮。
接口幂等解决办法
1.利用DB主键生成器或唯一性约束确保数据唯一
若重复请求,会触发Mysql(或者其他关系型数据库)唯一性异常SQLException:触发唯一性约束,返回成果表示。依托数据库。
流程:幂等字段查询是否有历史记录,有直接返回,无执行插入操作,结果1:顺利插入返回成功,结果2:有异常(DuplicateKeyException,并发已经处理过)反查数据,若已经有返回成功。
通常不建议:依赖insert,依赖异常(如果换数据库、spring版本等异常发生改变就会出问题),高并发依赖DB,给DB造成压力。
低并发+insert操作可用,高并发也要作为系统最后一道防线使用
2.额外状态字段,不通过DB实现,如工作流实现。
确定状态变化规则,只有已结束-进行中-已就绪,不可以已就绪-进行中
3.为当前业务生成唯一表示,客户端向令牌服务器申请唯一令牌如JWT,服务提供者会保持第一次处理成功结果或者失败的结果,之后将之前操作过的第一次结果返回。真正写入的业务操作只会产生一次。
4.本地消息事件表,一般用在MQ产品下,生产者的消息被消费者接收处理后,为每个消息设置稳定的MessageId,将第一次处理结果保存在DB,如果重复也是返回第一次被绑定的messageId结果。
乐观锁和悲观锁不可以处理幂等性接口,只可解决并发冲突问题,而不是消息缓存问题。能保证前后顺序一致,但不能保证结果一致。
5.高并发场景综合性最终方案:分布式锁一锁二判三更新。其中二判是基于上面几种基础方案通过状态机/唯一性索引/流水表做的重复操作判断。
注意分布式锁的颗粒度,否则还是会出现重复数据
声明式事务加锁(@Transactional)会在声明的方法全部执行完后提交事务,而如果把锁加在里面,锁的范围比方法的范围小,就会导致并发出现重复数据。
解决方法:通过编程式事务控制锁的颗粒度