阅读 772

Kafka的架构、Kafka消费和Kafka的Rebalance机制

在消息中间件技术选型的时候有很多选择,一个必须要回答的问题就是Kafka为什么可以脱颖而出?

本文就以Kafka的设计和发展为引子,试图从一个侧面解释Kafka为什么这么快,以及通过介绍Kafka消费消息的原理和过程,让你明白Kafka是如何实现支持海量并发、弹性和高可用的。

image.jpeg

Kafka的简介和起源

Kafka是一个流平台:在这个平台上可以发布和订阅数据流,并把它们保存起来、进行处理。

Kafka 有点像消息系统,允许发布和订阅消息流。从这点来看,它类似于 ActiveMQ、RabbitMQ、IBM MQSeries 等产品。

从另一个角度来看,我们可以把Kafka看成实时版的 Hadoop。Hadoop 可以存储和定期处理大量的数据文件,而 Kafka 可以存储和持续处理大型的数据流。

从Kafka的发展史可以看到,Kafka是最初由Linkedin公司开发,是一个分布式、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可以用于web/nginx日志、访问日志,消息服务等等,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。

Kafka主要设计目标如下:

  1. 以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能保证常数时间的访问性能。

  2. 高吞吐率,即使在非常廉价的商用机器上也能做到单机支持每秒100K条消息的传输。

  3. 支持Kafka Server间的消息分区,及分布式消费,同时保证每个partition内的消息顺序传输。

  4. 同时支持离线数据处理和实时数据处理。

  5. Scale Out,支持在线水平扩展。

Kafka通常应用于两大类应用:

1. 在应用和系统之间创建可靠的实时流数据处理管道。

2. 创建实时传递或响应数据流的流处理应用。

Kafka的消息传递模式

一个消息系统负责将数据从一个应用传递到另外一个应用,应用只需关注于数据,无需关注数据在两个或多个应用间是如何传递的。

分布式消息传递基于可靠的消息队列,在客户端应用和消息系统之间异步传递消息。

有两种主要的消息传递模式:点对点传递模式、发布/订阅模式,大部分的消息系统选用发布/订阅模式,Kafka就是一种发布/订阅模式。

在点对点消息系统中,消息持久化到一个队列中。此时,将有一个或多个消费者消费队列中的数据,但是一条消息只能被消费一次。

当一个消费者消费了队列中的某条数据之后,该条数据则从消息队列中删除。

该模式即使有多个消费者同时消费数据,也能保证数据处理的顺序。

image.jpeg

在发布/订阅消息系统中,消息被持久化到一个Topic中。与点对点消息系统不同的是,消费者可以订阅一个或多个Topic,消费者可以消费该Topic中所有的数据,同一条数据可以被多个消费者消费,数据被消费后不会立马删除。

在发布/订阅消息系统中,消息的生产者称为发布者,消费者称为订阅者。

该模式的示例图如下:

image.jpeg

Kafka的架构

Apache Kafka是一个分布式的流处理平台。

一个流处理平台拥有3个关键能力:

1. 发布订阅:能够发布和订阅记录的流,类似消息队列或企业级消息系统的能力。

2. 持久化:能够以容错性的持久化机制存储记录流。

3. 实时性:能够在记录流出现时处理它们。

下图展示了一个Kafka的架构图,然后分别介绍一下其中的元素和相关术语。

image.jpeg

1. Broker

Kafka 集群包含一个或多个服务器,服务器节点称为broker,broker存储topic的数据。

如果某topic有N个partition,集群有N个broker,那么每个broker存储该topic的一个partition。

如果某topic有N个partition,集群有(N+M)个broker,那么其中有N个broker存储该topic的一个partition,剩下的M个broker不存储该topic的partition数据。

如果某topic有N个partition,集群中broker数目少于N个,那么一个broker存储该topic的一个或多个partition。在实际生产环境中,尽量避免这种情况的发生,这种情况容易导致Kafka集群数据不均衡。

2. Topic

Topic类似于数据库的表名,每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。

物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处。

3. Partition

topic中的数据分割为一个或多个partition,每个topic至少有一个partition,每个partition中的数据使用多个segment文件存储。

partition中的数据是有序的,不同partition间的数据丢失了数据的顺序。

如果topic有多个partition,消费数据时就不能保证数据的顺序。在需要严格保证消息的消费顺序的场景下,需要将partition数目设为1。

4. Producer

生产者即数据的发布者,该角色将消息发布到Kafka的topic中。

broker接收到生产者发送的消息后,broker将该消息追加到当前用于追加数据的segment文件中。

生产者发送的消息,存储到一个partition中,生产者也可以指定数据存储的partition。

5. Consumer

消费者可以从broker中读取数据。

一个消费者可以消费多个topic中的数据。

6. Consumer Group

每个Consumer属于一个特定的Consumer Group。

可为每个Consumer指定group name,若不指定group name则属于默认的group。

7. Leader

每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition。

8. Follower

Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。

如果Leader失效,则从Follower中选举出一个新的Leader。

当Follower与Leader挂掉、卡住或者同步太慢,leader会把这个follower从“in sync replicas”(ISR)列表中删除,重新创建一个Follower。

主题分区和消费者群组

KafkaConsumer从Kafka订阅Topic,并从订阅的Topic拉取消息。

Kafka的消费者属于消费者群组,一个群组的消费者订阅的是同一个主题,每个消费者接收主题一部分分区的消息。

每个分区只能被消费者群组的一个消费者接收消息,如果一个消费者群组的消费者数多于一个主题的分区数,则多余的消费者会空闲。

如果主题T1有4个分区,那么消费者群组1有1个、2个、4个和5个消费者的话,与分区的连接情况分别如下:

image.jpeg

image.jpeg

image.jpeg

image.jpeg

分区再均衡Rebalance

分区的所有权从一个消费者转移到另一个消费者,这样的行为被称为再均衡。再均衡为Kafka的消息消费带来了高可用性和伸缩性,从而让我们可以放心地添加或者移除消费者,也能很好地处理单节点故障。

但是在再均衡期间,消费者无法读取消息,会造成整个群组的一小段时间不可用。另外,当分区被重新分配给另一个消费者时,消费者当前的读取状态会丢失,它可能还需要去刷新缓存,在它恢复状态之前会拖慢整个应用程序。因此,在正常情况下我们并不希望发生再均衡行为。

当一个新的消费者加入,原本从属其他消费者的分区可能分配到该消费者。例如原本消费者群组只有一个消费者,那么分区0到分区3这4个都是从属这一个消费者的,如果第二个消费者加入,那么分区2和分区3就会分配给第二个消费者。相反地,如果发生消费者的关闭或者崩溃,也会由其他消费者来接手原本从属于这个消费者的分区。

消费者通过向被指派为群组协调器的broker发送心跳来维持它们和群组的丛书关系以及它们对分区的所有权关系,消费者会在轮询(为了获取消息)或者提交偏移量的时候发送心跳,来证明自己是活跃的,如果消费者停止发送心跳的时间过长,那么会话就会过期,群组协调器认为它已经死亡,就会触发一次再均衡。

分配分区是怎样的一个过程,当消费者要加入群组时,它会向群组协调器发送 JoinGroup 请求。第一个加入群组的消费者将成为“群主”。群主从协调器那里获得群组的成员列表(列表中包含了所有最近发送过心跳的消费者,它们被认为是活跃的), 并负责给每一个消费者分配分区。它使用一个实现了 PartitionAssignor 接口的类来决定哪些分区应该被分配给哪个消费者。

Kafka为什么这么快?

在消息中间件技术选型的时候有很多选择,Kafka为什么可以脱颖而出?主要是Kafka无与伦比的性能表现。比如在某电商网站的线上活动中,Kafka 搭建的日志集群单个Topic可以达到几百万的TPS,而使用RocketMQ组件的核心业务集群,集群TPS只能达到几十万TPS。

但性能也并不是考虑技术选型的唯一标准,还有从功能性上来考虑,例如 RocketMQ 提供了丰富的消息检索功能、事务消息、消息消费重试、定时消息等。从业务的角度考虑,通常在大数据、流式处理场景基本选用 Kafka,业务处理相关选择 RocketMQ。

Kafka 中文件的布局是以 Topic/partition ,每一个分区一个物理文件夹,在分区文件级别实现文件顺序写,如果一个 Kafka 集群中拥有成百上千个主题,每一个主题拥有上百个分区,消息在高并发写入时,其 IO 操作就会显得零散,其操作相当于随机 IO,即 Kafka 在消息写入时的 IO 性能会随着 topic 、分区数量的增长,其写入性能会先上升,然后下降

而 RocketMQ 在消息写入时追求极致的顺序写,所有的消息不分主题一律顺序写入 commitlog 文件,并不会随着 topic 和 分区数量的增加而影响其顺序性。但通过笔者的实践来看一台物理机并使用 SSD 盘,但一个文件无法充分利用磁盘 IO 的性能。

两者文件组织方式,除了在磁盘的顺序写方面有所区别后,由于其粒度的问题,Kafka 的 topic 扩容分区会涉及分区在各个 Broker 的移动,其扩容操作比较重,而 RocketMQ 数据存储是基于 commitlog 文件的,扩容时不会产生数据移动,只会对新的数据产生影响,RocketMQ 的运维成本对 Kafka 更低。

最后 Kafka 的 ack 参数可以类比 RocketMQ 的同步复制、异步复制。Kafka 的 ack 参数为 1 时,对比 RocketMQ 的异步复制;-1 对标 RocketMQ 的 同步复制,而 -1 则对标 RocketMQ 消息发送方式的 oneway 模式。

Kafka 在消息发送方面比 RokcetMQ 有一个显著的优势就是消息格式的组织是发生在客户端,这样会有一个大的优势节约了 Broker 端的 CPU 压力,客户端“分布式”的承接了其优势,其架构方式有点类似 shardingjdbc 与 MyCat 的区别。

Kafka 在消息发送端另外一个特点是引入了双端缓存队列,Kafka 无处不在追求批处理,这样显著的特点是能提高消息发送的吞吐量,但与之带来的是增大消息的响应时间,并且带来了消息丢失的可能性,因为 Kafka 追加到消息缓存后会返回成功,如果消息发送方异常退出,会带来消息丢失。

Kafka 中的 linger.ms = 0 可类比 RocketMQ 消息发送的效果。

但 Kafka 通过提供 batch.size 与 linger.ms 两个参数按照场景进行定制化,比 RocketMQ 灵活。

例如日志集群,通常会调大 batch.size 与 linger.ms 参数,重复发挥消息批量发送机制,提高其吞吐量;但如果对一些响应时间比较敏感的话,可以适当减少 linger.ms 的值。

文章分类
后端
文章标签