RocketMQ、KafKa和RabitMQ对比

MQ消息队列的作用

  1. 解耦

解耦是消息队列要解决的最本质问题。

  1. 最终一致性

最终一致性指的是两个系统的状态保持一致,要么都成功,要么都失败。有些消息队列是不能保证最终一致性。

  1. 广播

消息队列必备的功能是可以进行广播的。有了消息队列,我们只需要关心消息是否送达了队列,至于谁希望订阅,是下游的事情,无疑极大地减少了开发和联调的工作量。

  1. 错峰与流控

典型的使用场景就是秒杀业务用于流量削峰场景。

由于篇幅的关系,本文重点介绍消息队列比较,详细应用场景请参考流量削峰漏斗模型:《什么是流量削峰?如何解决秒杀业务的削峰场景》

RocketMQ、KafKa和RabitMQ对比

特性 ActiveMQ RabbitMQ RocketMQ KafKa
开发语言 java erlang java scala
单机吞吐量 万级 万级 10万级 10万级
时效性 ms级 us级 ms级 ms级以内
集群管理方式 name server zookeeper
高可用性 高(主从架构) 高(主从架构)采用镜像模式实现,数据量大时会有性能瓶颈 非常高(分布式架构) 非常高(分布式架构)
主从切换 自动切换,最早加入集群的slave会成为master,因为新加入的slave不会同步master之前的数据,所以可能会出现部分数据丢失。 不支持自动切换,master失效后不能向master发送消息,consumer大概30s(默认)可感知此事件,此后从slave消费;如果master无法恢复,异步复制时可能会出现部分信息丢失 自动切换,N个副本,允许N-1个失效;master失效后自动从slave选择一个为主
消息写入性能 RAM约为RocketMQ的1/2,DISK的性能约为RAM性能的1/3。 很好,每条10个字节测试,单机单broker约7w/s,单机3broker约12w/s 非常好,每条10个字节测试:百万条/s
功能特性 成熟的产品,在很多公司得到应用,有较多的文档;各种协议支持较好 基于erlang开发,所以并发能力很强,性能极好,延迟很低,管理界面较丰富。 MQ功能较完备,扩展性较佳 只支持主要的MQ功能,像消息查询回溯等功能没有提供,大数据场景使用广泛。

为啥RabbitMQ能做到us级,其他的都是ms级呢?

主要有以下几个原因。

  1. erlang语言天然支持高并发。
  2. 基于内存镜像模式实现,RabbitMQ的us级主要指基于RAM模式。

RocketMQ、KafKa和RabitMQ选择

  1. Kafka

Kafka主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输,适合产生大量数据的互联网服务的数据收集业务。

大型公司建议可以选用,如果有日志采集功能,肯定是首选kafka了。

  1. RocketMQ

天生为金融互联网领域而生,对于可靠性要求很高的场景,尤其是电商里面的订单扣款,以及业务削峰,在大量交易涌入时,后端可能无法及时处理的情况。

RoketMQ在稳定性上可能更值得信赖,这些业务场景在阿里双11已经经历了多次考验,如果你的业务有上述并发场景,建议可以选择RocketMQ。

  1. RabbitMQ

RabbitMQ,结合erlang语言本身的并发优势,性能较好,社区活跃度也比较高,但是不利于做二次开发和维护。

如何保证消息的一致性和如何进行消息的重试机制?

如何保证消息的一致性

保证消息的一致性有三种方式。

  1. 定时补偿+幂等消费(TCC)

  2. 推拉结合

  3. 分布式事务

如何进行消息的重试机制

下面只分析RocketMQ的消息重试。

RocketMQ只有当消费模式为集群模式时,Broker才会自动进行重试,对于广播消息是不会重试的。消费者从Broker拉取消息失败时,RocketMQ会通过消息重试机制重新投递消息,这肯定不会一直投递。当投递到最大重试次数之后,就会加入到死信队列,我们只要对死信队列的数据做人工补偿。

认定为消费失败规则

RocketMQ规定,以下三种情况统一按照消费失败处理并会发起重试。

  1. 业务消费方返回ConsumeConcurrentlyStatus.RECONSUME_LATER
  2. 业务消费方返回null
  3. 业务消费方主动/被动抛出异常

注意 对于抛出异常的情况,只要我们在业务逻辑中显式抛出异常或者非显式抛出异常,broker 也会重新投递消息,如果业务对异常做了捕获,那么该消息将不会发起重试。因此对于需要重试的业务,消费方在捕获异常的时候要注意返回 ConsumeConcurrentlyStatus.RECONSUME_LATER 或 null 并输出异常日志,打印当前重试次数。(推荐返回ConsumeConcurrentlyStatus.RECONSUME_LATER)

个人不建议捕获异常,让MQ认定为失败。

RocketMQ重试时间窗口

messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

死信的业务处理方式

默认的处理机制中,如果我们只对消息做重复消费,达到最大重试次数之后消息就进入死信队列了。

我们也可以根据业务的需要,定义消费的最大重试次数,每次消费的时候判断当前消费次数是否等于最大重试次数的阈值。

如:重试三次就认为当前业务存在异常,继续重试下去也没有意义了,那么我们就可以将当前的这条消息进行提交,返回 broker 状态ConsumeConcurrentlyStatus.CONSUME_SUCCES,让消息不再重发,同时将该消息存入我们业务自定义的死信消息表,将业务参数入库,相关的运营通过查询死信表来进行对应的业务补偿操作。

RocketMQ 的处理方式为将达到最大重试次数(16 次)的消息标记为死信消息,将该死信消息投递到 DLQ 死信队列中,业务需要进行人工干预。实现的逻辑在 SendMessageProcessor 的 consumerSendMsgBack 方法中,大致思路为首先判断重试次数是否超过 16 或者消息发送延时级别是否小于 0,如果已经超过 16 或者发送延时级别小于 0,则将消息设置为新的死信。死信 topic 为:%DLQ%+consumerGroup。

发送失败如何重试

设置生产者在 3s 内没有发送成功则重试 3 次的代码如下。

   /**同步发送消息,如果 3 秒内没有发送成功,则重试 3 次*/
    DefaultMQProducer producer = new DefaultMQProducer("DefaultProducerGroup");
    producer.setRetryTimesWhenSendFailed(3);
    producer.send(msg, 3000L);

参考

https://www.jianshu.com/p/fec054f3e496
https://gitbook.cn/books/5d340810c43fe20aeadc88db/index.html

发布了12 篇原创文章 · 获赞 2 · 访问量 670

猜你喜欢

转载自blog.csdn.net/gonghaiyu/article/details/103956694