图解Kafka线程模型及其设计缺陷

1052次阅读  |  发布于3年以前

采用何种线程交互模型,如何高效率的提高网络处理能力是面向网络编程中一个非常重要的议题。

深入研究Kafka Broker服务端线程模型也是理解Kafka工作机制必备不可少的一环。

本文的探讨主要分成如下三个部分:

1、网络相关的配置参数

Kafka Broker端与网络相关的线程主要被分成network、IO两类线程,与之对应的是Kafka分别提供了两个参数用来设置其线程个数,分别如下:

那什么是网络线程,什么又是IO线程呢?请带着上述疑问,进入本文的学习交流中来。

2、线程模型探究

笔者崇尚“眼见为实”,故喜欢对其源码进行分析,从而提炼总结,故本文的探究手段还是以源码阅读为主,同时为了提高可读性,将提炼各种流程图。

理解上述几个参数的含义,通常运用的手段是查看这些参数的调用链,根据上下文进行理解与分析。

num.io.threads参数的使用调用链如下图所示:

num.network.threads参数的调用链如下图所示:

从这里我们可以得出如下两个重要的推断:

接下来将目光锁定在KafkaServer上。

2.1 IO线程工作机制

从上文的调用栈,我们不难找到使用num.io.threads的具体使用代码如下图所示:

核心实现要点总结如下:

通过对上述代码进行解读,我想不难得出如下时序图:

那RequestChannel中的待处理请求从哪来呢?

2.2 NetWork线程工作机制

network线程的初始化代码在SocketServer的createDataPlaneAcceptorsAndProcessors方法,详情如下图所示:

其核心要点如下所示:

为了方便大家理解,同样给出工作的顺序图如下:

2.3 图解Kafka线程模型

源码讲解确实比较抽象,接下来结合笔者对源码的阅读,总结提炼出Kafka线程模型如下图所示:

总结要点如下:

3、关于线程模型的一点思考

我们再次来总结一下Kafka线程模型中的几类线程(类比主从多Reactor模型):

Kafka的线程模型毫无疑问采取的是网络编程领域最经典的主从多Reactor模型,但个人觉得上述实现存在一个比较大的缺陷:业务线程隔离性不足,换句话说就是请求无优先级,容易相互影响

然后值得关注的是在Kafka2.2版本中引入来数据面、控制面的概念,用来隔离kafka内部的控制命令与数据命令:

但笔者觉得上述隔离程度远远不够,就拿客户端心跳包请求、数据拉取请求来说,如果多个消费组都去消费过早的数据,导致pagecache未命中,需要从磁盘加载数据,读磁盘如果出现瓶颈,会导致客户端端心跳请求无法及时处理,Broker在10s内没有收到(准确来讲是10s内没有触发心跳包处理流程),将导致消费组由于心跳超时而被Broker标记为宕机,从而触发重平衡,导致消费组无法消费,并且容易造成雪崩,该集群中所有消费组全部不可消费,其影响可想而知。

在这里不得不和RocketMQ来做一个横向对比,RocketMQ的线程处理模型就支持不同的命令类型使用不同的线程池,消息发送处理线程池、消息拉取线程池就分别拥有独立的线程池,起到了线程资源隔离的效果,不至于由于一类请求处理缓慢而导致其他更加重要的命令处于饥饿停滞不前,造成不可估量的后果。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8