Kafka学习笔记(十) --- Consumer端多线程开发消费者实例

  • Kafka Java Consumer的设计原理

Kafka Java Consumer采用的是单线程设计,为什么要采用单线程设计?其实严格来说,说单线程设计是不严谨的。因为,从0.10.1.0版本开始,KafkaConsumer就变成了双线程的设计,即用户主线程和心跳线程。

所谓用户主线程,就是你启动Consumer应用程序main方法的那个线程,而心跳线程只负责定期给Broker机器发送心跳请求,以标识消费者应用的存活性。引入心跳线程还有一个目的,那就是期望它能将心跳频率和主线程调用KafkaConsumer.poll方法的频率分开,从而解耦真实的消息处理逻辑与消费者组成员的存活性管理。不过,虽然有心跳线程,但是实际消息获取逻辑依然在用户主线程,因此从这个层面,依然可以认为KafkaConsumer是单线程设计。

老版本中Consumer是多线程的架构,每个Consumer实例在内部为所有订阅的主题分区创建对应的消息获取线程,也称Fetcher线程。老版本Consumer同时也是阻塞式的,实例启动后,内部会创建很多阻塞式的消息获取迭代器。但在很多场景下,Consumer端是有非阻塞需求的,比如流处理应用中执行过滤、连接、分组等操作就不能是阻塞式的消息获取。基于这个原因,社区为新版本Consumer设计了单线程+轮询的机制。

这样,Consumer端获取到消息后,处理消息的逻辑是否采用多线程,完全由你决定。另外,单线程设计比较容易实现,不是所有语言都很好滴支持多线程设计,所以这样的设计能使Consumer更容易移植到其他语言上。

  • 多线程方案

下面具体分析一下KafkaConsumer这个类的使用方法,以及如何推演出对应的多线程方案。

首先,需要明确的是KafkaConsumer类不是线程安全的。所有网络IO处理都是在用户主线程逻辑中,所以你不能在多线程中共享同一个KafkaConsumer实例,否则程序会抛出ConcurrentModificationException异常。但是有个方法是例外的,wakeup(),你可以在其他线程中安全滴调用它来唤醒Consumer。

鉴于KafkaConsumer不是线程安全的,可以制定两套多线程方案。

  1. 消费者程序启动多线程,每个线程维护专属的KafkaConsumer实例,负责完整的消息获取、消息处理流程。
  2. 消费者程序使用单或多线程获取消息,同时创建多线程执行消息处理逻辑,以实现消息获取和消息处理的真正解耦。

方案1是粗粒度化的工作划分,而方案2则更细粒度化。这两种方案各有千秋,下面的表格展示了两种方案的优缺点:

                                                

下面在展示一下两种方案的代码示例:

方案1:

扫描二维码关注公众号,回复: 8585829 查看本文章

                                                  

方案2:

                                                               

标注:这个系列文章是本人在极客时间专栏---kafka核心技术与实战中的学习笔记

    https://time.geekbang.org/column/article/101171

发布了37 篇原创文章 · 获赞 20 · 访问量 4955

猜你喜欢

转载自blog.csdn.net/qq_24436765/article/details/102541841