前言
通过《rocketmq概述》基本上对消息队列有着一定的了解,但有一种吞吐量更高的消息队死kafka。
集群结构
kafka的集群架构和rocketmq不尽相似
主要分为zookeeper集群、Broker集群、Producer集群和Consumer集群四个部分。
Broker集群
broker负责接收来自生产者的消息,为消息设置偏移量,并提交消息到磁盘保存。
- 每个broker是一个*立的 Kafka 服务器,启动时会把相关信息注册到zk中。
- 每个集群都有 broker 同时充当了集群控制器的角色(zk选举),控制器负责管理工作,包括将分区分配给 broker 和监控 broker.
- Kafka主题来组织数据,每个主题被分为若干个分区,每个分区有多个副本。那些副本被保存在 broker 上,每个broker可以保存成百上千个属不同主题和分区的副本。副本有以下两种类型
- 首领副本,每个分区都有一个首领副本 为了保证一致性,所有生产者请求和消费者请求都会经过这个副本。
- 跟随者副本,首领以外的副本都是跟随者副本。跟随者副本不处理来自客户端的请求,它们唯一的任务就是从首领那里复制消息,保持与首领一致的状态。如果首领发生崩渍,其中的跟随者会被提升为新首领。
- kafka中也引入主题的概念(topic), 在通过集群控制器创建主题时,集群控制器会根据[分区数;复制系数] 再结合集群中的broker数,才决定如何在 broker 间分配分区。
假设你有6个broker ,打算创建1个包含10 分区并且复制系数为3的主题?换算成分区副本数即3*10。在进行分区分配时,我们要达到如下的目标。
- broker平均分区副本。就是要保证每个 broker 可以分到5个副本
- 确保每个分区的每个副本分布在不同的broker 上。假设此分区的首领副本在 broker2上,那么可以把跟随者副本放在 broker3 或者 broker4 上,但不能放在 broker2 上,也不能两个都放在 broker3 上。
- 如果为 broker 指定了机架信息,那么尽可能把每个分区的副本分配到不同机架的 broker上。这样做是为了保证一个机架的不可用不会导致整体的分区不可用。
Kafka 生产者
生产者负责创建消息,并向要往 broker 写入消息。
- 生产者通过“bootstrap.sever”参数中给定的 broker里查找到其他broker 的信息(zk里面保存着broker及分区所有信息)。
- 生产者通过分区器指定的对应主题的分区
- 获取到对应分区信息后,消息被添加到一个记录批次里,这个批次里的所有消息都是相同的主题和分区的。统一发送:
- 同步发送消息,
- 异步发送消息,异步发送消息的同时能够对异常情况进行处理,生产者提供了回调支持
- acks确认机制,生产者怎么判断消息写入是成功的。
- acks=0 生产者在成功写入悄息之前不会等待任何来自服务器的响应。
- acks=1 ,只要集群的首领节点收到消息,生产者就会收到 个来自服务器的成功响应。
- acks=all ,只有 所有参与复制的节点全部收到消息时,生产者才会 个来自服务器的成功响应。
Kafka 消费者
消费者订个或多个主题,并按照消息生成的顺序读取它们。
-
生产者通过“bootstrap.sever”参数中给定的 broker里查找到其他broker 的信息(zk里面保存着broker及分区所有信息)。
-
Kafka消费者一帮是以群组方式消费。一个群组里的消费者订阅的是同 个主题,每个消费者接收主题部分分区的消息。
-
消费者通过向被指派为群组协调器的 broker发送心跳来维持它们和群组的从属关系以及它们对分区的所有权关系。只要消费者以正常的时间间隔发送心跳,就被认为是活跃的。然后进行“每一个分区对应仅有一个消费者”分配“,其分配策略尽可能消费者均衡”
-
消费者通过,poll()方式拉取记录列表,每条记录都包含了记录所属主题的信息、记 在分区的信息 记录在分区里的偏移量 ,以及记录的键值对。
-
消费者往一个叫作 consul’ler_offset 特殊主题发送消息,消息里包含每个分区的偏移量(到zk中)
- 同步提交:会一直尝试直至提交成功。如果提交失败会异常
- 异步提交:发送提交请求然后继续做其他事情,如果提交失败,错误信息和偏移量会被记录下来
zookeeper集群
zookeeper的作用是注册中心,存储了一些关于 consumer 和 broker 的状态信息及其topic相关配置。主要作用:
- 维护broker和consumer心跳状态
- 记录broker中topic及其Partition相关配置
- 消息组的消费进度Offset 记录
- 主备broker的leader 选举 和 follower 信息同步
存储结构
如果所示:
- kafka中每个分区对应一个文件
- 又因大文件里查找和删除消息是很费时的,也很容易出错,所以我们把分区分成若个片段。默认情况下,每个片段包含 lGB或者1周的数据,以较小的那个为准。如果达到片段上限,就关闭当前文件,井打开 个新文件。
当前正在写入数据的片段叫作活跃片段。 - 消费者可以从 Kafka 的任意可用偏移量位置开始读取消息。为了帮助 broker 更快地定位到指定的偏移量, Kafka为每个分区维护了1个索引。索引把偏移量映射到片段文件和偏移量在文件里的位置。
最佳可靠的实践
Kafka 的复制机制和分区的多副本架构是 Kafka 可靠性保证的核心。
- 将题级别的复制系统是设置成N。在N-1个 broker 失效的情况下,仍然能够从主题读取数据或向主写入数据
- 关闭不完全的首领选举,当分区首领不可用时 一个同步副本会被选为新首领。如果此副本数据不完全,则不让选。
- 设置最少同步副本为2.因为关闭了不完全的首领选举,所以必须保证一个副本数据完全。对过此参数,消息只有在被写入到同步副本数量大于最少同步副本才被认为是已提交的。
- 将生产者acks=all,保证主副本同步副本数量大于最少同步副本才返回成功
与RocketMq的比较
-
文件布局
- Kafka 中文件的布局是以 Topic/partition ,每一个分区一个物理文件夹,在分区文件级别实现文件顺序写,如果一个Kafka集群中拥有成百上千个主题,每一个主题拥有上百个分区,消息在高并发写入时,其IO操作就会显得零散,其操作相当于随机IO,即 Kafka 在消息写入时的IO性能会随着 topic 、分区数量的增长,其写入性能会先上升,然后下降。
- RocketMQ在消息写入时追求极致的顺序写,所有的消息不分主题一律顺序写入 commitlog 文件,并不会随着 topic 和 分区数量的增加而影响其顺序性。但一个文件无法充分利用磁盘IO的性能。
- 除了在磁盘的顺序写方面有所区别后,由于其粒度的问题,Kafka 的 topic 扩容分区会涉及分区在各个 Broker 的移动,其扩容操作比较重,而 RocketMQ 数据存储是基于 commitlog 文件的,扩容时不会产生数据移动,只会对新的数据产生影响,RocketMQ 的运维成本对 Kafka 更低。
-
ack机制
Kafka 的 ack 参数可以类比 RocketMQ 的同步复制、异步复制。Kafka 的 ack 参数为 1 时,对比 RocketMQ 的异步复制; -1 对标 RocketMQ 的 同步复制,而 -1 则对标 RocketMQ 消息发送方式的 oneway 模式。 -
数据写入方式
- Kafka 的消息写入使用的是 FileChannel
- RocketMQ 的消息写入支持 内存映射 与 FileChannel 写入两种方式
-
消息发送方式
- Kafka 在消息发送客户端采用了一个双端队列,引入了批处理思想。消息会首先存入到一个双端队列中,然后会单独开一个 Send 线程,从双端队列中获取一个发送批次,将消息按批发送到 Kafka集群中。
- RocketMQ 消息发送在客户端主要是根据路由选择算法选择一个队列,然后将消息发送到服务端
- Kafka 在消息发送方面比 RokcetMQ 有一个显著的优势就是消息格式的组织是发生在客户端。这样会有一个大的优势节约了 Broker 端的CPU压力。
总结,从上面的对比来看,Kafka 在性能上综合表现确实要比 RocketMQ 更加的优秀,但在消息选型过程中,我们不仅仅要参考其性能,还有从功能性上来考虑,例如 RocketMQ 提供了丰富的消息检索功能、事务消息、消息消费重试、定时消息等。个人认为通常在大数据、流式处理场景基本选用 Kafka,业务处理相关选择 RocketMQ。
主要参考
《kafka权威指南》
《Kafka 架构原理解析》
《Kafka与RocketMQ性能对比大揭秘》