rabbitmq,kafka,rocketmq对于消息中间件需要解决问题处理方式的对比

参考RocketMQ用户指南 v3.2.4

1. 发布订阅模式-publish/subscribe

rabbitmq

rabbitmq不仅支持发布-订阅模式,而且由于其使用了exchange,把消息和接收消息的queue解耦了,支持更多的操作,如exchange有fanout,direct,topic,对应queue有简单队列,work queue,订阅模式,路由模式以及主题模式

kafka

支持发布-订阅模式,集群发送consumer group,以及广播发送(不同的consumer group)

rocketmq

两种消费模式,集群发送以及广播发送

2. 消息优先级-message priority

需求:有些数据,比如说,一些特别紧急的活动对应的数据,需要尽快的反应到页面中。跟其他的消息混杂的一个队列中,势必需要去等待队列中其他的普通消息先处理完了,才能轮到自己

所以一般来说,也会单开高优先级的队列,然后如果业务系统有高优先级的消息,直接写到高优先的队列中,这样的话呢,后续流程全部单独处理

rabbitmq

rabbitmq也是已追加的方式写入文件的。并且提供一个index来方便查询。其是支持优先级队列的,设置queue为优先级队列,给消息加优先级。

kafka

因为kafka an ordered log,所以不能实现消息优先级

rocketmq

由于RocketMQ所有消息都是持久化的,所以如果按照优先级来排序,开销会非常大,因此RocketMQ没有特意支持消息优先级,但是可以通过变通的方式实现类似功能,即单独配置一个优先级高的队列,和一个普通优先级的队列,将不同优先级发送到不同队列即可。

对于优先级问题,可以归纳为2类

1) 只要达到优先级目的即可,不是严格意义上的优先级,通常将优先级划分为高、中、低,或者再多几个级别。每个优先级可以用不同的topic表示,发消息时,指定不同的topic来表示优先级,这种方式可以解决绝大部分的优先级问题,但是对业务的优先级精确性做了妥协。

2) 严格的优先级,优先级用整数表示,例如0 ~ 65535,这种优先级问题一般使用不同topic解决就非常不合适。如果要让MQ解决此问题,会对MQ的性能造成非常大的影响。这里要确保一点,业务上是否确实需要这种严格的优先级,如果将优先级压缩成几个,对业务的影响有多大?

3. 顺序消息-message order

总结起来,要实现严格的顺序消息,简单且可行的办法就是:

保证生产者 - MQServer - 消费者是一对一对一的关系

否则总是会出现一些问题,这一点朱忠华老师已经讲得恨透彻了

石杉老师也讲了一点,参考

如何保证消息的顺序性?

RabbitMQ

kafka

保证同一个partition,这样也不影响多个partition的并行度

但是这样一个消费者启用多个线程,也可能存在问题,主要是

1. 处理链路变长,导致offset位移管理困难

2. worker thread线程异常,使用了内存queue,可能导致消费数据丢失

rocketmq

保证同一个队列,这样也不影响多个message queue的并行度

4. 消息过滤-message filter

rabbitmq

由于加入了routing key,对这项支持很好

kafka

broker端过滤

Consumer可以直接通过topic name去订阅消息,也可通过白名单或黑名单告诉kafka,我要消费哪些数据。如果Consumer通过Topic Filter创建消息流,则它会同时在/brokers/topics上也创建Watch,来看topic的变化情况

 

consumer端过滤

这种过滤方式可由应用完全自定义实现,但是缺点是很多无用的消息要传输到Consumer端。

rocketmq

rocketmq设计上在topic下面,还有tag

 

broker端过滤

RocketMQ支持按照简单的Message Tag过滤,也支持按照Message Header、body进行过滤。

consumer端过滤

这种过滤方式可由应用完全自定义实现,但是缺点是很多无用的消息要传输到Consumer端。

5. 消息持久化-message persistence

消息中间件通常采用的几种持久化方式:

(1). 持久化到数据库,例如Mysql。

(2). 持久化到KV存储,例如levelDB、伯克利DB等KV存储系统。

(3). 文件记录形式持久化,例如Kafka,RocketMQ

rabbitmq

队列索引rabbit_queue_index 和消息存储(rabbit_msg_store),文件形式存储

kafka

文件形式记录持久化

rocketmq

文件形式记录持久化,RocketMQ参考了Kafka的持久化方式,充分利用Linux文件系统内存cache来提高性能。

6. 消息可靠性-message reliability

影响消息可靠性的几种情况:

(1). Broker正常关闭

(2). Broker异常Crash

(3). OS Crash

(4). 机器掉电,但是能立即恢复供电情况。

(5). 机器无法开机(可能是cpu、主板、内存等关键设备损坏)

(6). 磁盘设备损坏。

rabbitmq

haproxy+keepalived+cluster镜像队列模式+federation,所以不会丢失数据,而且应该没有master的概念,三个node都是master,只是有一个node为stats node

kafka

由于partition做了leader和follower,通过同步双写技术可以完全避免单点,所以不会丢失数据

rocketmq

(1)、(2)、(3)、(4)四种情况都属于硬件资源可立即恢复情况,RocketMQ在这四种情况下能保证消息不丢,或者丢失少量数据(依赖刷盘方式是同步还是异步)。

(5)、(6)属于单点故障,且无法恢复,一旦发生,在此单点上的消息全部丢失。RocketMQ在这两种情况下,通过异步复制,可保证99%的消息不丢,但是仍然会有极少量的消息可能丢失。通过同步双写技术可以完全避免单点,同步双写势必会影响性能,适合对消息可靠性要求极高的场合,例如与Money相关的应用。

RocketMQ从3.0版本开始支持同步双写。

7. 消息低延迟-low latency messaging

rabbitmq

consumer有broker push和poll两种,一般使用broker push,通过prefetch count来控制大小,spring-amqp就是这种方式

kafka

通过consumer.poll自己控制轮询时间。

默认spring-kafka的@KafkaListener类似最长时间轮询,这样消息到达broker后,能立刻到达Consumer

rocketmq

在消息不堆积情况下,消息到达Broker后,能立刻到达Consumer。

RocketMQ使用长轮询Pull方式,可保证消息非常实时,消息实时性不低于Push。

8. 必须投递一次-at least once

是指每个消息必须投递一次

rabbitmq

RocketMQConsumer先broker push消息到本地,消费完成后,才向broker返回ack,如果没有消费一定不会ack消息,所以可以很好的支持此特性。

kafka

kafka默认就是at least once

rocketmq

RocketMQConsumer先pull消息到本地,消费完成后,才向服务器返回ack,如果没有消费一定不会ack消息,所以RocketMQ可以很好的支持此特性。

9. 无重复消息-exactly only once

(1). 发送消息阶段,不允许发送重复的消息。

(2). 消费消息阶段,不允许消费重复的消息。

rabbitmq

rabbitmq没有发送幂等性,不处理

consumer幂等性自己来做

kafka

kafka通过producer的幂等性实现不发送重复的消息

consumer的幂等性需要我们自己根据业务来实现

rocketmq

rocketmq不处理producer的发送消息的幂等性?

RocketMQ不保证消息不重复,如果你的业务需要保证严格的不重复消息,需要你自己在业务端去重。

 

只有以上两个条件都满足情况下,才能认为消息是“Exactly Only Once”,而要实现以上两点,在分布式系统环境下,不可避免要产生巨大的开销。所以RocketMQ为了追求高性能,并不保证此特性,要求在业务上进行去重,也就是说消费消息要做到幂等性。RocketMQ虽然不能严格保证不重复,但是正常情况下很少会出现重复发送、消费情况,只有网络异常,Consumer启停等异常情况下会出现消息重复。

此问题的本质原因是网络调用存在不确定性,即既不成功也不失败的第三种状态,所以才产生了消息重复性问题。

10. broker的buffer/partition/queue满了怎么办

rabbitmq

会进行内存和磁盘的告警,并且rabbitmq.config中有相应的配置。一般就是hang住producer,也有垃圾回收机制

kafka

如果极端的情况下,消息装不下了,那kafka会hang住,一般情况下kafka会保留7天数据,之后有清理策略

rocketmq

对于此问题的解决思路,RocketMQ同其他MQ有非常显著的区别,RocketMQ的内存Buffer抽象成一个无限长度的队列,不管有多少数据进来都能装得下,这个无限是有前提的,Broker会定期删除过期的数据,例如Broker只保存3天的消息,那么这个Buffer虽然长度无限,但是3天前的数据会被从队尾删除。

11. 回溯消费 replay

rabbitmq

消息都持久化了,支持replay,也可能垃圾回收掉了

kafka

由于消息都持久化了,支持replay

consumer.beginningOffsets方法,beginning和0不一样,如果没有清理,那么是0,否则就不相同。

consumer.endOffsets方法,那么就开始消费最新的数据

consumer.seek方法

rocketmq

回溯消费是指Consumer已经消费成功的消息,由于业务上需求需要重新消费,要支持此功能,Broker在向Consumer投递成功消息后,消息仍然需要保留。并且重新消费一般是按照时间维度,例如由于Consumer系统故障,恢复后需要重新消费1小时前的数据,那么Broker要提供一种机制,可以按照时间维度来回退消费进度。

RocketMQ支持按照时间回溯消费,时间维度精确到毫秒,可以向前回溯,也可以向后回溯。

12. 消息堆积

消息中间件的主要功能是异步解耦,还有个重要功能是挡住前端的数据洪峰,保证后端系统的稳定性,这就要求消息中间件具有一定的消息堆积能力,消息堆积分以下两种情况:

(1). 消息堆积在内存Buffer,一旦超过内存Buffer,可以根据一定的丢弃策略来丢弃消息,如CORBA Notification规范中描述。适合能容忍丢弃消息的业务,这种情况消息的堆积能力主要在于内存Buffer大小,而且消息堆积后,性能下降不会太大,因为内存中数据多少对于对外提供的访问能力影响有限。

(2). 消息堆积到持久化存储系统中,例如DB,KV存储,文件记录形式。

 

当消息不能在内存Cache命中时,要不可避免的访问磁盘,会产生大量读IO,读IO的吞吐量直接决定了消息堆积后的访问能力。

评估消息堆积能力主要有以下四点:

(1). 消息能堆积多少条,多少字节?即消息的堆积容量。

(2). 消息堆积后,发消息的吞吐量大小,是否会受堆积影响?

(3). 消息堆积后,正常消费的Consumer是否会受影响?

(4). 消息堆积后,访问堆积在磁盘的消息时,吞吐量有多大?

 

kafka和rocketmq据说都能支持百万级的消息吞吐能力,这依赖于服务器的性能

13. 分布式事务

已知的几个分布式事务规范,如XA(XA是X/Open DTP组织(X/Open DTP group)定义的两阶段提交协议),JTA(Java Transaction API也是两阶段提交,可以看做是java在XA上的一种实现吧—自己理解)等。其中XA规范被各大数据库厂商广泛支持,如Oracle,Mysql等。

rabbitmq

支持事务,但由于对于吞吐量影响很大,所以producer一般还是使用confirm机制+补偿来处理

kafka

kafka的事务整体上看仍然是两阶段提交,但Kafka只提供对Kafka本身的读写操作的事务性,不提供包含外部系统的事务性

也就是说,其可以保证事务发送的多条msg全部成功或全部失败,其核心在于Transaction Marker的形式将COMMITABORT信息写入用户数据日志以及Offset Log

rocketmq

分布式事务涉及到两阶段提交问题,在数据存储方面的方面必然需要KV存储的支持,因为第二阶段的提交回滚需要修改消息状态,一定涉及到根据Key去查找Message的动作。RocketMQ在第二阶段绕过了根据Key去查找Message的问题,采用第一阶段发送Prepared消息时,拿到了消息的Offset,第二阶段通过Offset去访问消息,并修改状态,Offset就是数据的地址。

RocketMQ这种实现事务方式,可以实现本地事务+发送异步消息同时成功或者同时失败。

没有通过KV存储做,而是通过Offset方式,存在一个显著缺陷,即通过Offset更改数据,会令系统的脏页过多,需要特别关注。所以RocketMQ 在4.x 的版本中将这部分功能去除。系统中的一些上层Class都还在,用户可以根据实际需求实现自己的事务功能。

14. 定时消息  延迟队列

定时消息是指消息发到Broker后,不能立刻被Consumer消费,要到特定的时间点或者等待特定的时间后才能被消费。

什么情况下需要定时队列呢?

比如全量刷数据的情况,如本来有个字段,status,0,1,2这样的值,但是现在要将所有数据,status这个字段的值刷为OPEN,CLOSED,SEND之类的状态

所以一般对这个问题是这样的,我们会针对刷数据的问题,单独开出来队列,专门处理刷数据的请求,对这些队列的消费,通常来说,只会在凌晨0点之后才开始执行

如果要支持任意的时间精度,在Broker层面,必须要做消息排序,如果再涉及到持久化,那么消息排序要不可避免的产生巨大性能开销。

rabbitmq

延迟队列插件,所以很好的支持

kafka

不支持定时消息和延迟队列

rocketmq

RocketMQ支持定时消息,但是不支持任意时间精度,支持特定的level,例如定时5s,10s,1m等。

15. 消息重试

在集群环境下,可能造成重复消费。所以要保证消息消费幂等/去重

rabbitmq

producer端要自己来实现retry以及降级,比如存储下来,一个分布式定时任务来做补偿

kafka

producer端发送消息重试

参考:11. kafka重试机制解读

consumer端

消费端消费失败的重试,需要我们自己写

rocketmq

可以return CONSUME_LATER,其会应用retryMessageLevel

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值