一、消息的幂等
RocketMQ可以保证消息不丢失但是无法保证消息不重复。
- 当出现消费者对某条消息重复消费的情况时,重复消费的结果与消费一次的结果是相同的,并且多次消费并未对业务系统产生任何负面影响,那么这个消费过程就是消费幂等的。
消费一次和消费多次对系统产生影响相同。
重复的消息可能会影响业务处理,需要做幂等处理。 - 消息重复的场景
1)发送消息时消息重复:生产者向broker发送消息,broker网络闪断没有向生产者发送应答失败,生产者默认发送失败,将重复发送消息。
2)消费时消息重复:消费成功时向broker提交offset,由于网络闪断broker未发送成功响应,为了保证消息至少被消费一次原则,broker恢复网络后将再次向消费者发送该条消息。
3)rebalance时消息重复:consumer发生变化时,触发rebalance,消费者读取队列会按照上一个生产者处理的进度继续处理,由于offset是异步处理就会导致读取到的和时间消费的进度不一致,此时可能会导致重复消费。 - 解决幂等
两要素:幂等令牌(生产者与消费者既定协议,例如业务标识的字符串);唯一性处理(服务端通过采用算法策略,保证同一个业务逻辑不会被重复执行多次)。
1)缓存去重:在缓存中如果已经存在幂等令牌,说明本次操作是重复性操作,若缓存中没有,则进行下一步。
2)**唯一性处理之前,在数据库中查询幂等令牌作为索引的数据是否存在。**存在则为重复;不存在进行下一步。
3)同一事务中完成三项操作:唯一性处理后,将幂等令牌写入缓存,并将幂等令牌作为唯一索引的数据写入DB。
支付业务:先从redis缓存中获取支付流水号,没有的话从数据库中查询,如果有支付流水号则证明是重复消费。在分布式事务中完成支付任务,将支付流水号写入缓存和数据库。 - 消费幂等的实现
使用业务唯一标识作为幂等处理关键依据,业务唯一标识可以根据消息的key设置。
Message message = new Message();
message.setKey(“orderId_001”);
SendResult sendResult = producer.send(message);
消费者收到消息时可以根据消息的key即订单号来实现消息幂等。
msg.getKey();
一、消息堆积与延迟
- 消息堆积:消费者消费速度跟不上生产速度,这部分消息为堆积消息。堆积消息自然会造成消费延迟。一下场景需要重点关注消息堆积和消费延迟问题:
业务系统上下游能力不匹配造成持续堆积,且无法恢复。
业务系统对消息的消费实时性要求较高,即使是短暂的堆积造成的消费延迟也无法接受。 - 产生原因分析
两阶段:消息拉取、消息消费。
消费堆积主要在客户端的消费能力,消费能力由消费耗时和消费并发度。
消费耗时:代码逻辑,CPU内部计算代码和I/O操作型代码。例如复杂递归循环、读写外部数据库、外部缓存系统、下游系统调用,例如Dubbo的RPC远程调用,SpringCloud的对下游系统对Http接口调用。
消费并发度:由单节点线程数和节点数量共同决定,单节点线程数节点数量。
单节点线程数:单个Consumer所包含的线程数量。
节点数量:Consumer Group所包含Consumer数量。
对于普通消息、延时消息并发度计算都是单节点线程数节点数,对于顺序消息为Topic的queue分区数量。
全局顺序消息:该类型topic只有一个Queue分区。其可以保证该Topic的所有消息被顺序消费。为了保证这个全局顺序性,Consumer Group
分区顺序消息:只能保证Queue顺序消费,不能保证Topic顺序消费。 - 单机线程数计算
C * (T1 + T2) / T1
C:CPU内核数
T1:CPU内部逻辑计算耗时
T2:外部IO操作耗时
最优线程数=C*(T1+T2)/T1=CT1/T1+CT2/T1
三、消息清理
以commitlog文件为单位进行清理,默认72小时。
文件过期,到达清理时间点后,自动清理过期文件。
文件过期,且磁盘空间占用率已达到过期清理警绒线(默认为75%),无论是否达到清理时间点,都会自动清理过期文件。
磁盘占用率达到清理警绒线(默认85%)后,开始按照设定好的规则清理文件,无论是否过期。默认会从最老的文件开始清理。
磁盘占用率达到系统危险警绒线(默认90%)后,Broker将拒绝消息写入。