1 前言
因为才开始写博客 写的比较乱 我在这里对我上篇博客做一下总结吧
链接:对接第三方开发平台的一套模板思想_陆地剑仙-东的博客-CSDN博客
有兴趣咱们可以看下 没兴趣可以直接看下这个总结吧
总结
1 重试
重试分为rpc的重试和消息消费失败的重试,或者是偏移量重置等等的重试,还有就是基于job的重试,重试是为了保证最终一致
2 幂等
幂等是一次原子操作的唯一性,做成功了就不做了,并且多次重试结果一致,如果上游感知你的返回值就需要考虑,如果不感知也不是非得保证,这个有重试才有幂等,因为重试是一条链,中间某个节点重试失败会从头开始重试,所以之前重试过的节点需要做幂等 常见的实现方式是基于分布式请求id+数据库(nosql),就是先落库请求id,成功后更新状态,每次都判断该请求id是否状态为成功或者基于业务逻辑判断,例如提交订单,订单状态为已提交直接返回
1 请求id
我们自己制定还是上游制定.自己制定那么就是上游需要遵守你的规范,上游制定那就是你遵守上游的规范,我个人理解这也是一个依赖倒置思想的体现,我建议是由上游指定,就算你当前的逻辑比较确定,假设就是用订单号做请求Id,但是你保证不了以后你这个接口要给另外一个域调用,他们是用订单号+日期做幂等,这样你逻辑是要做适配的,但是上游给,你代码就是通用的
2 基于业务逻辑
基于业务逻辑就是说你这个逻辑也是固定的,是标准的,例如提交订单这个动作,不会说订单提交了还能再提交,这样也没意义,如果是一次加钱操作,就无法业务逻辑判断,因为业务逻辑允许钱一直加
3 什么请求用requestId,什么请求用业务逻辑幂等
1 当业务逻辑是类似的一个提交 但上游无法确保同样的参数给你同样的请求id的时候,就不能请求id,只能业务幂等
2 当业务逻辑是类似的一个提交,这种是可以用业务逻辑幂等,同时也可以用请求id幂等的时候,这就取决于谁来制定幂等规范
3 当业务逻辑是一次类似加钱或者是单据的回调 那这种无法使用业务幂等,只能用请求Id,这里插一句,为了防止用户看到报错,又点击了一次加钱按钮(一个新请求id),这种需要事务提交后基于领域事件驱动后续节点来做重试达到最终一致,达到用户无感知报错
4 当你的链路有分布式节点,例如mq或写redis或es之类的,他们的幂等只能用requetId,上游不给就需要自己根据业务逻辑造一个
3 锁,分布式锁(redis,zk)和基于版本号的乐观锁
分布式锁就不详细介绍了
乐观锁,基于读再写,写的时候必须数据库的version等于读的,compare and swap
锁的范围是读到version和事务提交这中间的时间,这里分2种
第一种是version在事务里面读,如果是可重复读 那锁的范围就是整个事务,如果是读已提交(那就是读到version到事务提交这段时间
所以建议锁从事务外面拿
4 重试和幂等的关系
重试和幂等是兄弟,没有重试就没有幂等
5 重试和锁的关系
没关系
6 锁和幂等关系
不考虑唯一索引的情况
结论: 先加锁,再幂等,如果是乐观锁,version字段建议从事务外面传进去,锁要在事务外面,因为事务里面做幂等依赖隔离级别,并且可能存在事务未提交,锁先释放,锁>事务和幂等
因为假设有A,B 2个请求同时到达,A和B先做幂等校验的话都校验不住,于是都进到方法里,这时候A抢到了锁,B刚好时间片轮转被夯了100ms,A执行完后刚好花了90ms,将锁释放,于是B也拿到了锁 开始执行业务逻辑
伪代码实现
1 如果非得在事务里面查询version 那要在中间查询下业务是否已经执行,这样才算锁的范围大于幂等的这段代码
public class Arepository{
@事务注解
public int updateA(OrderDo do){
1 读到version
//幂等逻辑
2 version控制
3 并发抛出异常
4 正常执行
}
}
2 就是version在事务外面读的传进去的,这种就是锁的范围包括了幂等逻辑,这样就没问题
public class Arepository{
//orderDO的version由上层从数据库查到了传进来的
@事务注解
@幂等注解或者业务逻辑
public int updateA(OrderDo do){
1 读到version
2 version控制
3 并发抛出异常
4 正常执行
}
}
以上都是我个人见解,发现错误请留言!