RocketMQ、KafKa和RabitMQ对比

MQ使用场景

使用 MQ 需要注意的问题,不要过度依赖 MQ,比如发送短信验证码或邮件等功能,这种低频但有可能比较耗时的功能可以使用多线程异步处理即可,不用任何的功能都依赖 MQ 中间件来完成,但像秒杀抢购可能会导致超卖(也就是把货卖多了,库存变成负数了)等短时间内高并发的请求,此时建议使用 MQ 中间件。

MQ消息队列的作用

  1. 解耦

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

  1. 最终一致性

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

  1. 广播

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

  1. 错峰与流控

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

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

RocketMQ、KafKa和RabitMQ对比

特性ActiveMQRabbitMQRocketMQKafKa
开发语言javaerlangjavascala
单机吞吐量万级万级10万级10万级
时效性ms级us级ms级ms级以内
集群管理方式name serverzookeeper
高可用性高(主从架构)高(主从架构)采用镜像模式实现,数据量大时会有性能瓶颈非常高(分布式架构)非常高(分布式架构)
主从切换自动切换,最早加入集群的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开发,所以并发能力很强,性能极好,延迟很低,管理界面较丰富。RabbitMQ 支持拉取数据模式和主动推送数据的模式,也就说 RabbitMQ 服务器会主动把消息推送给订阅的消费者;RabbitMQ 支持多租户的功能,也就是常说的 Virtual Host(vhost)MQ功能较完备,扩展性较佳Kafka 支持消息回溯,它可以根据 Offset(消息偏移量)、TimeStamp(时间戳)等维度进行消息回溯,而 RabbitMQ 并不支持消息回溯;Kafka 的消息消费是基于拉取数据的模式,也就是消费者主动向服务器端发送拉取消息请求;大数据场景使用广泛。

为啥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语言本身的并发优势,性能较好,社区活跃度也比较高,但是不利于做二次开发和维护。

RabbitMQ 集群是由多个节点组成,但默认情况下每个节点并不是存储所有队列的完整拷贝,这是出于存储空间和性能的考虑,因为如果存储了队列的完整拷贝,那么就会有很多冗余的重复数据,并且在新增节点的情况下,不但没有新增存储空间,反而需要更大的空间来存储旧的数据;同样的道理,如果每个节点都保存了所有队列的完整信息,那么非查询操作的性能就会很慢,就会需要更多的网络带宽和磁盘负载来存储这些数据。

为了能兼顾性能和稳定性,RabbitMQ 集群的节点分为两种类型,即磁盘节点和内存节点,对于磁盘节点来说显然它的优势就是稳定,可以把相关数据保存下来,若 RabbitMQ 因为意外情况宕机,重启之后保证了数据不丢失;而内存节点的优势是快,因为是在内存中进行数据交换和操作,因此性能比磁盘节点要高出很多倍。

如果是单个 RabbitMQ 那么就必须要求是磁盘节点,否则当 RabbitMQ 服务器重启之后所有的数据都会丢失,这样显然是不能接受的。在 RabbitMQ 的集群中,至少需要一个磁盘节点,这样至少能保证集群数据的相对可靠性。

如果集群中的某一个磁盘节点崩溃了,此时整个 RabbitMQ 服务也不会处于崩溃的状态,不过部分操作会受影响,比如不能创建队列、交换器、也不能添加用户及修改用户权限,更不能添加和删除集群的节点等功能。

对于 RabbitMQ 集群来说,我们启动集群节点的顺序应该是先启动磁盘节点再启动内存节点,而关闭的顺序正好和启动的顺序相反,不然可能会导致 RabbitMQ 集群启动失败或者是数据丢失等异常问题。## 如何保证消息的一致性和如何进行消息的重试机制?

如何保证消息的一致性

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

  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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值