高吞吐、高可用MQ对比分析
2016年11月04日

基本对比信息


ActiveMQRabbitMQRocketMQKafkaZeroMQ
吞吐量比RabbitMQ低2.6w/s(消息做持久化)11.6w/s17.3w/s29w/s
开发语言JavaErlangJavaScala/JavaC
主要维护者ApacheMozilla/SpringAlibabaApacheiMatix,创始人已去世
成熟度成熟成熟开源版本不够成熟比较成熟只有C、PHP等版本成熟
文档和注释很少较少很少
订阅形式点对点(p2p)、广播(发布-订阅)

提供了4种:direct, topic ,Headers和fanout。fanout就是广播模式


基于topic/messageTag以及按照消息类型、属性进行正则匹配的发布订阅模式基于topic以及按照topic进行正则匹配的发布订阅模式点对点(p2p)
持久化支持少量堆积支持少量堆积支持大量堆积支持大量堆积不支持
顺序消息不支持
不支持支持支持不支持
消息回溯不支持不支持支持指定时间点的回溯支持指定分区offset位置的回溯不支持
性能稳定性一般较差很好
负载均衡可以支持可以支持支持较好支持很好不支持
集群方式支持简单集群模式,比如'主-备',对高级集群模式支持不好。

支持简单集群,'复制'模式,对高级集群模式支持不好。

常用 多对'Master-Slave' 模式,开源版本需手动切换Slave变成Master

天然的‘Leader-Slave’无状态集群,每台服务器既是Master也是Slave

不支持
管理界面一般较好一般


而谈到消息系统的设计,就回避不了两个问题:

1、消息的顺序问题

2、消息的重复问题


关于这两方面,RocketMQ的测试结果如下:


一、消息顺序读写

参考文章:http://www.jianshu.com/p/453c6e7ff81c

1、常规的理解是:在同一个队列(分区)的消息是按顺序排列的。但测试结果如下:

单服务器单个线程的Producer异步生产数据,生产的数据都用同一个字符串做hash,数据都分配在同一台机器的同一个队列。

单服务器单Consumer配置1个或者10个线程一起消费,每次最多抓取1条或者1014条数据,结果:同一个线程同一批次取回来的数据,顺序却不一致。

例如,取回来的数据预期顺序是:12、13、14、15、16……但结果却是:13、14、15、16、12……

同一批次测试,所有环境都一样,有一定几率(大概15%)会出现该情况。

 

原因:Producer异步生产数据会导致数据乱序。即使是单线程的Producer。

 

解决方案:为保证生产的消息顺序一致,Producer需采用同步发送数据,但是性能会大大下降(实测1851条/秒,同等情况下比kafka慢45%,不过一般情况下这性能也足够了)

 

二、消息不丢失、不存在重复发送

根据阿里的测试结论,RocketMQ和Kafka在 消息不丢失 方面,做得差不多,服务器基本上不会丢失数据。结论如下:

1、在Broker进程被Kill的场景(在消息收发过程中,利用Kill -9 命令使Broker进程终止), Kafka和RocketMQ都不丢消息,可靠性都比较高。

2、在宿主机掉电的场景,在“同步刷盘”策略下,Kafka与RocketMQ均能做到不丢消息,在“异步刷盘”策略下,Kafka和RMQ都无法保证掉电后不丢消息。

参考来源:http://jm.taobao.org/2016/04/28/kafka-vs-rocktemq-4/

 

在 消息不重复 方面,RocketMQ和Kafka估计都差不多,在kill或者宕机时,都会存在数据重复,而且RocketMQ官方说明,RocketMQ无法保证数据不重复,建议通过业务手段来保证。



实际应用场景分析(RocketMQ)

 

1、保证消息按顺序写入和读取

1)顺序写入(前提:单机,或者 集群但是每个节点来源数据不同)

原理:

  在同一个队列(分区)的消息是按顺序排列的。(跟Kafka一样,只不过kafka的分区为物理分区,rocketMQ的队列是逻辑分区)

故,要保证消息按顺序处理,必须把它们放在同一个队列中。

 

实现方式:但producer同步发送数据,按orderid hash来区分队列存放数据。

 

单线程版:RocketMQ使用单线程同步写入消息到topic里面,topic使用消息类型区分,appid用tags区分,可以在消息中添加orderid关键字。

例如:

Message(topic->"abc", tags->"appid11111", keys->"M324343ef", content);

Message(topic->"cdb", tags->"appid33333", keys->"M684343ef", content);

 

多线程版:每个线程操作不同appId的数据。实测单机普通CPU多线程无益于提高producer的效率。(i7-CPU测试结果为:两线程比单线程提高36%,三线程比两线程提高12%,三线程CPU已爆满)

 

2)按顺序消费

RocketMQ的消费方式有两种,一种是传统的自动化的push推模式(对应的DefaultMQPushConsumer),服务器端主动推送消息。另一种是pull拉模式(对应的DefaultMQPullConsumer),客户端从指定位置主动去服务器拉取数据。

RocketMQ 的 推 模式原理:源码分析(略)

问题:Consumer可以设置消费线程的数量,但是在顺序消费的模式(MessageListenerOrderly)下,多个消费线程对于同一队列的数据,无法并行运行,仍然是串行执行,以达到顺序处理数据的目的。对多个不同队列的数据,消费线程可以并行执行。

 

例如:producer同步发送数据,按orderid hash来区分队列存放数据。假设一个topic,有999个orderid分配到999个队列,

假设consumer端有50个消费线程,那么这50个线程就可以并行的消费999个队列里面的数据。

 

3)与kafka对比

顺序写入:kafka producer同步发送数据,按orderid hash来区分队列存放数据。假设一个topic,有999个orderid分配到8个分区,

顺序消费:kafka consumer都订阅所有topics,但是consumer线程受制于partition数量的限制,假设为8,那么最多8个线程并行消费数据,这8个线程并行的消费8个partition里面的数据。

 

关键之处,kafka和rocketMQ的区别在于一个用物理分区来存放数据,一个按虚拟队列的存放数据,都能保证队列或分区里面数据的有序性。

据阿里中间件 官方给出的测试结论,RocketMQ虚拟队列的效率 高于kafka 物理分区的效率,当kafka的分区数大于8时,kafka性能快速下降,而rocketMQ支持1万队列而不会降低性能。

 

集群模式:

假设有4台master的RocketMQ ,

每台RocketMQ 的consumer都订阅所有topic,

假设一个topic,有999个appid分配到999个队列,

假设每台RocketMQ consumer端有50个消费线程,那么这4*50=200个线程就可以并行的消费999个队列里面的数据。

 

假设有4台broker的Kafka,

每台kafka的consumer都订阅所有topic,

假设一个topic,有999个orderid分配到8个分区,

总的consumer线程受制于partition数量的限制,假设为8,那么最多8个线程(4*2,每台broker2个线程)并行消费8个partition里面的数据。

 

显然,RocketMQ 更占优势,如果kafka要达到RocketMQ 这种效果,需要扩展到200个分区才能使200个线程同时消费。

  

 

开源版本RocketMQ存在的问题

1、主从不能自动切换,导致了一系列小问题。

     1)普通消息当主挂了,会拉取从上面的消息,最后如果主又活过来了,会导致重复消费(之前消费的进度在从上面)。

    2)主down,顺序消息不能写入,不能消费(顺序消费不能消费slave上面的消息)。顺序消息是写入特定的queue,这个queue所在的机器挂了,就不能写了,手动写入其他的queue,在业务来看,也是有问题。

2、rocketmq 对于消息重复的处理能力是不足且没有保障的,文档中也明确说明需要业务去重。

 

3、对于指定位置消费,RocketMQ 的PushAPI比较低级,需要自己实现消费位置的保存、节点的拉取和维护等。

 

Github上有300多个issues,坑很多,而且看评论,都没有及时解决。

 

相比Kafka,在Apache下,有完善的测试,能看到存在的bug和fix状态,以及比较详细的说明。比如:

https://issues.apache.org/jira/browse/KAFKA-4189?jql=project%20%3D%20KAFKA%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20consumer%20ORDER%20BY%20priority%20DESC



推荐一篇关于RocketMQ非常棒的文章:

http://www.jianshu.com/p/453c6e7ff81c#