系统一直使用的Kafka用来消费业务流水,最近在群里听到反馈说希望转到Rocketmq,支持更多的业务功能,因此去了解了一下,发现相比之下Kafka确实应用场景
太少了,基本就是消费日志,相比之下Rockmq有很多优秀的特性,下面具体介绍一下。
首先介绍一下Kafka,Kafka是一个分布式、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统,比较常用于日志收集系统,和消息系统等
topic类似数据库的表名,代表一个类别,一个topic中的数据分为多个partition,每个partition中的数据由多个segment文件存储,每个partition中的数据是有序的,因此如果
想保证有序,需要将topic的partition数目设置为1
1、可以以时间复杂度为O(1)的方式提供消息持久化能力,对TB以上的数据也能保持常数时间的访问性能
2、高吞吐率
3、支持Kafka server间的消息分区,保证每个partition内的消息顺序传输
4、支持离线数据处理和实时数据处理
5、支持在线水平扩展
Kafka的高可用(HA High Availability):
通过多副本机制实现,每个partition的数据都会同步到其他机器上,形成自己的多个replica副本,然后所有副本选举出一个leader,
生产和消费都只和leader交互,其他副本都是follower,写的时候,producer写leader,leader将数据写入本地磁盘,然后其他follower来pull数据,全部同步好以后发送ack给leader,返回返回成功,所有follower即为ISR,即可用且消息量与leader相差不多的副本集合
这种情况下如果某个broker宕机了,如果他是leader,那么其他副本会重新选举出一个leader,继续使用新的leader,保证了高可用性
Kafka的高性能:
1、批处理,从生产者发送到消费者消费,Kafka都进行了批处理操作,即生产者发送一条消息无论同步还是异步,Kafka都不会立即把消息发送出去,而是先放到内存中,将多个消息组成一个批消息,发送到broker端,在broker端也不会拆开,而是做为批消息写入磁盘,同步到其他副本,消费者也是这个批消息,然后在客户端将批消息还原为一条条消息
buffer.memory:33554432 (32m) 调整未发送出去消息的缓冲区大小
batch.size:16384一个批次的数据字节大小,太大占用缓冲区多,但是不会延迟(有其他触发机制)而 太小会频繁提交消息效率低下
2、消息压缩 compression.type:none 默认发送不压缩,可以配置合适的压缩方式(snappy/gzip/lz4可选,推荐 少CPU,高性价比的snappy)
3、顺序读写提升磁盘性能,如果磁盘随机读写,需要进行寻址,而Kafka使用顺序读写
4、pagecache加速读写,pagecache是操作系统给磁盘文件在内存中建立的缓存,当进程准备读取文件内容时,操作系统首先看待读取的数据所在的页是否在页缓存中,如果不在,就需要读取磁盘,然后将数据同步到页缓存,Kafka同样提供了同步刷盘,即避免因为宕机导致页缓存数据还未完成同步导致的数据丢失,但是实际没必要考虑这个因素,因为消息可靠性可以由多副本来解决,同步刷盘会带来性能的影响
Page Cache是系统级别的缓存(内核缓冲区),当上层有写操作时,操作系统只是将数据写入pagecache,同时标记page属性为dirty,当读操作发生时,先从pagecache中查找,如果发生缺页才进行磁盘调度,pagecache避免了在JVM内部缓存数据,避免了不必要的GC,以及内存空间占用,如果Kafka重启,它会失效,而操作系统管理的PageCache依然可以继续使用。
producer把消息发到broker后,数据并不是直接落入磁盘的,而是先进入PageCache。PageCache中的数据会被内核中的处理线程采用同步或异步的方式写回到磁盘。Consumer消费消息时,会先从PageCache获取消息,获取不到才回去磁盘读取,并且会预读出一些相邻的块放入PageCache,以方便下一次读取如果Kafka producer的生产速率与consumer的消费速率相差不大,那么几乎只靠对broker PageCache的读写就能完成整个生产和消费过程,磁盘访问非常少。
5、零拷贝,Kafka通过将pagecache中的数据直接复制到socket的缓冲区,减少了内核态到用户态再到socket缓冲区的复制过程
传统的网络I/O过程:
1.操作系统从磁盘把数据读到内核区2.用户进程把数据从内核区copy到用户区
3.然后用户进程再把数据写入到socket,数据流入内核区的Socket Buffer上
4.最后把数据从socket Buffer中发送到到网卡,这样完成一次发送
可以发现,同一份数据在内核Buffer与用户Buffer之间拷贝两次:
但是通过SendFile(又称zero copy)优化后,直接把数据从内核缓冲区copy到socket缓冲区,然后发送到网卡,避免了在内核Buffer与用户Buffer来回拷贝的弊端:
不仅是Kafka,Java的NIO提供的FileChannle,它的transferTo、transferFrom方法也利用了这种在内核区完成数据传输的功能。
RocketMQ由NameServer注册中⼼集群、Producer⽣产者集群、Consumer消费者集群和若⼲Broker(RocketMQ进程)组成,它的架构原理是这样的:
1. Broker在启动的时候去向所有的NameServer注册,并保持⻓连接,每30s发送⼀次⼼跳
2. Producer在发送消息的时候从NameServer获取Broker服务器地址,根据负载均衡算法选择⼀台服务器来发送消息
3. Conusmer消费消息的时候同样从NameServer获取Broker地址,然后主动拉取消息来消费
rocketmq没有使用zookeeper的原因:
1. 根据CAP理论,同时最多只能满⾜两个点,⽽zookeeper满⾜的是CP,也就是说zookeeper并不能保证服务的可⽤性,zookeeper在进⾏选举的时候,整个选举的时间太⻓,期间整个集群都处于不可⽤的状态,⽽这对于⼀个注册中⼼来说肯定是不能接受的,作为服务发现来说就应该是为可⽤性⽽设计。
2. 基于性能的考虑,NameServer本身的实现⾮常轻量,⽽且可以通过增加机器的⽅式⽔平扩展,增加集群的抗压能⼒,⽽zookeeper的写是不可扩展的,⽽zookeeper要解决这个问题只能通过划分领域,划分多个zookeeper集群来解决,⾸先操作起来太复杂,其次这样还是⼜违反了CAP中的A的设计,导致服务之间是不连通的。
3. 持久化的机制来带的问题,ZooKeeper 的 ZAB 协议对每⼀个写请求,会在每个 ZooKeeper 节点上保持写⼀个事务⽇志,同时再加上定期的将内存数据镜像(Snapshot)到磁盘来保证数据的⼀致性和持久性,⽽对于⼀个简单的服务发现的场景来说,这其实没有太⼤的必要,这个实现⽅案太重了。⽽且本身存储的数据应该是⾼度定制化的。
4. 消息发送应该弱依赖注册中⼼,⽽RocketMQ的设计理念也正是基于此,⽣产者在第⼀次发送消息的时候从NameServer获取到Broker地址后缓存到本地,如果NameServer整个集群不可⽤,短时间内对于⽣产者和消费者并不会产⽣太⼤影响。
rocketmq的事务
实现原理如下:
1. ⽣产者先发送⼀条半事务消息到MQ
2. MQ收到消息后返回ack确认
3. ⽣产者开始执⾏本地事务
4. 如果事务执⾏成功发送commit到MQ,失败发送rollback
5. 如果MQ⻓时间未收到⽣产者的⼆次确认commit或者rollback,MQ对⽣产者发起消息回查
6. ⽣产者查询事务执⾏最终状态
7. 根据查询事务状态再次提交⼆次确认
最终,如果MQ收到⼆次确认commit,就可以把消息投递给消费者,反之如果是rollback,消息会保存下来并且在3天后被删除。
相比于Kafka,rocketmq具有的优势主要有:
1、支持事物型消息(消息发送和db操作保持两方的一致性)
A,B(均存在DB操作)两方需保证事物一致性,通过引入中间层MQ,A和MQ保持一致性(异常情况下通过MQ反查A接口实现check),B和MQ保持一致性(通过重试),
从而达到最终事务一致性(大事务=小事务+异步)
2、支持结合rocketmq的多个系统之间数据最终一致性(多方事物)
3、支持18个级别的延迟消息
4、支持指定次数和时间间隔的失败消息重发
5、支持consumer端tag过滤,减少不必要的网络传输
6、支持重复消费(这个Kafka也支持,理论上可以按照offset来回溯消息)
7、rocketmq的注册中心为name server,Kafka为zookeeper
与Kafka的优劣对比
1、单机吞吐量: Kafka比rocketmq要高,这事Kafka最大的优点,Kafka会根据topic和partition创建物理文件,partition的数量和对应的物理文件是一一对应的
rocketmq只有一个物理文件,即commitLog,queue的数量是在commitLog里体现的,真正存储消息的只有commitLog一个文件,由于Kafka的topic一般有多个patition,因此Kafka的数据写入速度比rocketmq高出一个量级,但是如果topic过多,导致写入的文件数量过多,会导致原先的顺序写转为随机写,性能急剧下降
Kafka是基于分区,每个分区一个文件,而rocketmq是基于队列,只有一个文件,即commitLog
Kafka的tps能达到单机百万,主要是因为producter端将多个小消息合并,批量发向broker
而rocketmq没这么做的原因是:
2、topic数量: rocketmq可以支持成百上千的topic,而Kafka在达到几百个topic的时候吞吐量就会大幅下降,因为Kafka的topic数量不能太多,受到zookeeper协调策略和消息文件存储设计的制约
3、rocketmq支持异步刷盘,同步刷盘,同步replication,异步replication,Kafka使用异步刷盘,异步replication,同步刷盘在单机可靠性上比Kafka更高,不会因为操作系统crash导致数据丢失,Kafka的replication以topic为单位,支持主机宕机,备机自动切换,但是由于是异步replication,切换后可能会有数据丢失,同时如果leader重启,会与已经存在的leader产生冲突, rocketmq不支持master宕机,slave自动切换为master,即master宕机以后,slave只能提供读
发送消息:kafka默认使用异步发送的形式,有一个memory buffer暂存消息,同时会将多个消息整合成一个数据包发送(batch.size),这样能提高吞吐量,但对消息的实效有些影响;rocketmq可选择使用同步或者异步发送。
发送响应:kafka的发送ack支持三种设置:0 消息存进memory buffer就返回;1 等到leader收到消息返回,all 等到leader和isr的follower都收到消息返回,当然kafka都是异步刷盘。rocketmq都需要等broker的响应确认,有同步刷盘,异步刷盘,同步双写,异步双写等策略,相比于kafka多了一个同步刷盘。
4、rocketmq在队列上保持有序,同时也可以设置普通顺序还是严格顺序,Kafka只支持partition上有序
5、broker端消息过滤,Kafka不支持,RocketMQ支持两种Broker端消息过滤方式
推荐博客:
rocketmq和Kafka区别
https://blog.csdn.net/zuojunyuan/article/details/108883234
rocketmq介绍
http://www.dockone.io/article/9726