这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战」
总结
从分析到解决思路,我们不难发现:如果需要比较严格地保证幂等性,就需要结合悲观锁和乐观锁的思想来做设计。
说是悲观锁和乐观锁,其实并不是很贴切的描述,具体使用的是:
- 悲观锁:
+ 通过分布式锁,来锁住执行过程
+ 通过一个全局唯一的id,来作为锁的唯一键
- ”乐观“锁:
+ 这里称为乐观锁其实不完全正确,在这里我们用了2个字段来校验:
- 全局唯一id,这个可以是上面悲观锁的唯一键,也可以是其他的东西,也可以是某些字段的组合,反正必须是唯一的!
- 不可回滚的状态:这个状态标识了当前处理进度以及处理情况,通过上面的唯一键和这个不可回滚的状态,才可以通过CAS的方式来做一个前置的判断。
> 这个**状态值**是否可以去除?
>
>
> **答**:如果我们的处理结果只有两种情况:初始创建以及处理完成,那么其实这个字段可以用记录的**有无**来取代;
>
>
> 但**如果**我们可能会处理失败,那么此时我们需要**让后续请求尝试重新处理该业务**,那么就多了一个**失败**的状态;
>
>
> 而正因为我们需要让后续请求**可以重试**,那么还需要一个状态来表示当前正在处理中,让后续的请求知晓当前并不是失败,而是有别的请求正在执行。
>
>
> 因此,这个状态,一般情况下不可去除。
>
>
> 新建状态处理中完成失败
那么回到上面的问题:
- 我们对哪个字段设计幂等?
- 放到订单中就是:我们希望哪个字段是幂等的?
- 比如付款中,我们期望付款这个行为是幂等的,那么我们的幂等就需要通过上面”乐观锁”里所说的唯一键来保证,也可以说就是全局唯一id加上用户标识,来记录是否幂等,那么我们需要让哪个字段幂等呢?其实也就很明显:幂等的必须是表示状态的字段。
- 幂等字段如何设计?那么也简单,全局唯一id,唯一键呗,再扯下去不就是分布式id啊UUID雪花算法之类的了吗?
这里也可以看出来,幂等保证的核心,其实就在于唯一,并且要保证全局唯一,通过这个全局唯一,我们才能去上锁,去判断请求是否成功。
其实根据幂等性设计,面试官还问到了一个问题:
- 其实一般来说幂等性的设计都会涉及到MQ,那么如何保证MQ消息的可靠性呢?
其实后面想了一下,根据这个幂等性,也好理解:
- 我们在消费端和生产端都记录一下消息的发送状态,方便后续比对、重发(当然,这是属于兜底的部分了)。
- 消费端如果幂等处理完了,那么同样放回MQ,让原本的生产端,更新消息发送的处理情况。
+ 那么如果再出问题了呢,如何保证?这就是无穷的套娃问题了,这里的问题就涉及到:
+ kafka丢消息怎么处理?
+ 数据库故障怎么处理?
+ 服务器故障怎么处理?
- 是服务器卡了吗?
* 卡了的话,怎么排查?
- 还是服务器挂了?
* 服务器挂了是不是要做容灾啊?
* 怎么快速回滚?
* 是不是要做熔断?
+ redis故障怎么处理?
因此,设计上的问题其实大部分都不止一个点,一般来说都是由点及面的。
为了时空上的优越性我们采用了很多的中间件
例如:为了节约空间上的成本,我们会用专门的数据库:存储例如mySQL,缓存如redis。
为了解耦、削峰等操作,我们用kafka。
为了开发便利,我们会用Spring,mybatis等等。
这些东西大大地简化了我们把我们的想法落地的成本。但引入了更多的中间件就不可避免地要投入更多的精力去维护这些组件,因此,一个成熟想法的提出,总是要把这些边界条件考虑清楚的。
本文转载自: 掘金