一、背景
在网络不可靠的背景下,消息的投递和Ack都存在不确定性,消息中间件通常采用的方式是重试,保证至少消费一次,那么消费者需要保证幂等性
二、解决方案
常见的幂等:
- 业务表保存上游唯一键
- 领域状态机CAS,单向流转
- 本地消息表
- 事件版本号
2.1 业务表插入上游唯一键
此方案和本地消息表方案类似,都是向rds
插入一条数据。 不同之处在于这条数据通常还承载了业务其他信息
适用场景: 新增型的事务
劣势: 需要业务场景支持,幂等跟业务操作是耦合的
2.2 领域状态机
此方案和业务表插入上游唯一键方案类似 不同之处在于这条数据是属于UP
操作
使用场景:
- 更新型事务
- 存在状态机
- 状态机的流转是单向的
劣势: 需要业务场景支持,幂等跟业务操作是耦合的
2.3 本地消息表
此方案需要上游传入bizModule
和bizKey
消息头信息, 消费者服务需要建立本地消息表,将业务操作和幂等插入在一个原子性事务内
基于存储实现方案类型:
- 基于
rds
方案 - 基于
nosql
方案
rds方案
需要数据支持跨表事务,需要插入数据操作(仅存储bizModule
、 bizKey
),
优势:
- 保证仅消费一次
- 不侵入业务
- 不需要后期补数据
劣势:
- 在并发场景高的场景,会影响系统性能
使用场景:
- 对数据可靠性要求高业务
- 性能不敏感
- 业务操作也落地在
rds
中
nosql方案
通常的nosql
选型是redis
,本地消息表数据存储在redis
中, 使用setNx
方法,保证幂等性,可靠性比rds
低一些
优势:
- 性能高
劣势:
- 无法保证完全可靠,在极端场景需要补偿消费
使用场景:
- 对性能敏感
- 存在数据对账补偿程序,保证可靠性
- 业务操作不落地在
rds
中
三、平台化
平台提供SDK
组件,基于注解启用,抽象幂等组件,提供基础RDS
和Redis
实现的幂等组件,注解支持自定义幂等组件,统一标准化消息参数,生产端加入bizModule
和bizKey
消息头属性赋值,消费端拦截监听器的handler
,先校验幂等再执行业务方法