Kafka Consumer1
本文的代码基于kafka的0.10.1
的版本。
重新设计的原因
0.9以前的consumer是通过zookeeper来进行状态管理里的。
- 羊群效应
- 任何Broker或者Consumer的增减都会触发所有的Consumer的Rebalance
- Split brain(大脑分裂)
- 每个Consumer分别单独通过Zookeeper判断哪些Broker和Consumer 宕机了,那么不同Consumer在同一时刻从Zookeeper“看”到的View就可能不一样,这是由Zookeeper的特性决定的,这就会造成不正确的Reblance尝试。
- 调整结果不可控
- 所有的Consumer都并不知道其它Consumer的Rebalance是否成功,这可能会导致Kafka工作在一个不正确的状态。
实现目标
在0.9, kafka 重新设计了 consumer,主要有以下几个目的:
-
更轻便的客户端
- 部分用户希望开发和使用non-java的客户端。现阶段使用non-java发SimpleConsumer比较方便,但想开发High Level Consumer并不容易。因为High Level Consumer需要实现一些复杂但必不可少的失败探测和Rebalance。如果能将消费者客户端更精简,使依赖最小化,将会极大的方便non- java用户实现自己的Consumer。
-
一个集中的协调者
- 如上文所述,当前版本的High Level Consumer存在Herd Effect和Split Brain的问题。如果将失败探测和Rebalance的逻辑放到一个高可用的中心Coordinator,那么这两个问题即可解决。同时还可大大减少 Zookeeper的负载,有利于Kafka Broker的Scale Out。
-
允许手动分配partition
- 有些系统希望将 partition 和 consumer 进行一一对应。
- 在一些高级的消费者中,他们希望停止自动的 rebalance。
- 这个实现依赖于集中的协调者。
-
允许手动管理 offset
- 一些系统希望以特定的时间间隔在自定义的数据库中管理Offset。这就要求Consumer能获取到每条消息的metadata,例如 Topic,Partition,Offset,同时还需要在Consumer启动时得到每个Partition的Offset。实现这些,需要提供新的 Consumer API。同时有个问题不得不考虑,即是否允许Consumer手工管理部分Topic的Offset,而让Kafka自动通过Zookeeper管理其它 Topic的Offset。一个可能的选项是让每个Consumer只能选取1种Offset管理机制,这可极大的简化Consumer API的设计和实现。
-
允许用户指定 callback 和 rebalance
- 一些应用可能会在内存中为每个Partition维护一些状态,Rebalance时,它们可能需要将该状态持久化。因此该需求希望支持用户实现并指定一些可插拔的并在Rebalance时触发的回调。如果用户使用手动的Offset管理,那该需求可方便得由用户实现,而如果用户希望使用Kafka提供的自动Offset管理,则需要Kafka提供该回调机制
-
非阻塞的消费API
- 该需求源于那些实现高层流处理操作,如filter by, group by, join等,的系统。现阶段的阻塞式Consumer几乎不可能实现Join操作。
Partition分配
当多个consumer 去消费topic的消息时,首先面临的问题就是哪个consuemr应该消费那些topicAndPartition。可以调用assign
手动分配partition。如果需要手动分配,Kafka提供了两种自动分配 的方式,分别为Range
和roundrobin
。
Range
Range策略是对每个主题而言的,首先对同一个主题里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序。在我们的例子里面,排完序的分区将会是0, 1, 2, 3, 4, 5, 6, 7, 8, 9;消费者线程排完序将会是C1-0, C2-0, C2-1。然后将partitions的个数除于消费者线程的总数来决定每个消费者线程消费几个分区。如果除不尽,那么前面几个消费者线程将会多消费一个分区。在我们的例子里面,我们有10个分区,3个消费者线程, 10 / 3 = 3,而且除不尽,那么消费者线程 C1-0 将会多消费一个分区,所以最后分区分配的结果看起来是这样的:
- C1-0 将消费 0, 1, 2, 3 分区
- C2-0 将消费 4, 5, 6 分区
- C2-1 将消费 7, 8, 9 分区
假如我们有11个分区,那么最后分区分配的结果看起来是这样的:
- C1-0 将消费 0, 1, 2, 3 分区
- C2-0 将消费 4, 5, 6, 7 分区
- C2-1 将消费 8, 9, 10 分区
假如我们有2个主题(T1和T2),分别有10个分区,那么最后分区分配的结果看起来是这样的:
- C1-0 将消费 T1主题的 0, 1, 2, 3 分区以及 T2主题的 0, 1, 2, 3分区
- C2-0 将消费 T1主题的 4, 5, 6 分区以及 T2主题的 4, 5, 6分区
- C2-1 将消费 T1主题的 7, 8, 9 分区以及 T2主题的 7, 8, 9分区
可以看出,C1-0 消费者线程比其他消费者线程多消费了2个分区,这就是Range strategy的一个很明显的弊端。
RoundRobin strategy
RoundRobin 策略会将所有的topicAndPartition 和 消费者进行排序。然后循环遍历每个消费者,依次分配tp。
举例两个消费者C1 和 C2, 两个 topic , t0 和 t1, 每个 topic都有3个partition,这样下来总共有t0p0, t0p1, t0p2, t1p0, t1p1, and t1p2。而最终分配的结果是:
- C0 将消费 t0p0, t0p2, t1p1 这三个分区
- C1 将消费t0p1, t1p0, t1p2 这三个分区
如果有的消费者没有订阅其中的topic, 轮训策略会使得topic分配不均匀。
有的时候效果还不如 range assigment。
ConsumerCoordinator
当集群或者消费组出现变化的时候,就会执行partition的分配,我们称这种情况为 reblance。下面几种情况都会触发reblance:
- 新的consumer加入了。
- consumer 挂了。
- consumer 执行了 unsubscribe,离开这个消费组。
- 集群中的coordinator挂了,选举了一个新的。
- topic 增加了 partition。
这些情况都是由 ConsumerCoordinator 进行处理。Coordinator通过一个特定的 broker 对 单个 消费组(consumer group) 进行管理。 Coordinator 主要负责下面几件事情:
- Group Registration : 消费组里面的每个成员会通过自己的 metadata 向 coordinator 进行注册。
- Group/Leader Selection: Coordinator 会从消费组里面的选取一个成员作为一个leader。这个leader 会进行partition的分配,并将分配结果提交给 coordinator。
- State Assignment: Coordinator 会收集消费组里面每个成员的 metadata 信息,然后为它们分配不同的状态。
- Group Stabilization: 消费组里面的每个成员会通过coordinator接收到 leader 设计的分配状态,并开始处理数据。
图1 ConsumerCoordinator 状态图 ConsumerCoordinator
图1中,consumer 首先查找服务器的 coordinator, 然后通过ensureActiveGroup
成功加入消费组里面,并启动heartBeat 线程,当heartBeat线程的通信返回错误后,重新进行上述操作。当然也可能该consumer离开了消费组,这时候其它的consumer会重复上述操作。
这个里面最重要的应该就是ensureActiveGroup
这个动作,它主要负责和服务器的Coordinator node 进行交互,并根据返回的结果做相应的处理。
- 寻找服务器的coordinator。
- 启动HeartBeat线程。
- 将consumer加入group。
- 发送JoinGroupRequest 。
- 调用JoinGroupResponseHandler 处理 response。
- 如果结果成功了,查看返回结果中是不是指定这个consumer 作为leader。如果是leader,那就执行partition分配策略,如果不是,就说明是follower,不执行分配策略。
然后leader 和 follower 都发送SyncGroupRequest给coordinator,不同的是leader的参数里包含了分配策略。 - 如果失败了,根据不同的情况进行处理。
- 如果结果成功了,查看返回结果中是不是指定这个consumer 作为leader。如果是leader,那就执行partition分配策略,如果不是,就说明是follower,不执行分配策略。
- 处理 SyncGroupResponse,从其中获取consumer 消费的topicAndPartition,并触发 ConsumerRebalanceListener。
图2 展示了ensureActiveGroup
的具体行为。
参考:
[1] Kafka设计解析(四):Kafka Consumer解析
[2] Kafka分区分配策略(Partition Assignment Strategy)
posted on 2017-07-27 22:09 walkwalkwalk 阅读(334) 评论(0) 编辑 收藏 举报