写在前面
本篇主要说明了源码学习过程中服务端的相关知识点。通过本章节的学习,
1、对服务端的网络、存储、副本同步、集群管理相关的细节又回顾了一篇,比之前死记硬背好很多。
2、感觉很多架构的设计还是来源于实际需求,当然kafka的核心点就是:异步、削峰、解耦。
注意:
1、Kafka网络设计,理解超高并发的网络设计
2、juc,跳表,log【存储】,内存映射写稀松索引,时间轮机制,后续需要持续跟进。
1、服务端—网络
参考链接:深度剖析:Kafka 请求是如何处理? 看完这篇文章彻底懂了!
Kafka 服务端的网络设计通常采用三层架构,它包括以下三层:
网络层:Acceptor负责接受来自客户端的连接请求,并创建对应的网络连接。
请求层:Processor 负责处理来自客户端的请求,并将其传递给合适的 Handler 进行处理。它处于请求处理的中间层,负责请求的分发和路由。
处理层:Handler 接收到 Processor 分发的请求后,负责解析请求、执行相应的操作,并生成响应返回给客户端
1.1、服务端acceptor线程启动
-
入口函数:main;完成参数设置,调用启动函数
包名kafka.Kafka
-
kafka服务端启动的核心方法在
startup()
中,Start up API for bringing up a single instance of the Kafka server.
,启动NIO服务端
kafka.server.KafkaServer
-
SocketServer启动后,创建acceptor线程
位置1:endpoints取决于配置kafka的时候,在config/server.properties
里面的配置个数,例如:kafka1:9092,一般设置一个服务实例就可以
位置2:创建一个acceptor对象,继承AbstractServerThread
,Acceptor 是整个网络通信的入口点之一,负责处理客户端的连接请求,从而允许客户端与 Kafka 集群进行通信
位置3:Utils是一个工具类,里面由一个newThead方法,帮我们启动线程使用
-
run()方法中,执行逻辑就像JavaNIO中服务端轮询查看是否有请求接入
位置1:ServerChannel向Selector注册一个OP_ACCEPT事件,表示Selector会检查ServerChannel中是否有新的请求到达
位置2、位置3:Selector遍历注册的Key,如果key=OP_ACCEPT
,调用accept()接收请求
位置4:创建SocketChannel,以轮询的方式放入到不同的Processor线程
1.2、服务端Processor线程
1.2.1、Processor线程启动
- 创建位置和Acceptor一样,不过这里并没有启动Processor线程
processorBeginIndex:0
numProcessorThreads:默认值是3,由参数num.network.threads
控制
- Acceptor构造的时候,会在其主构造函数中执行(Scala),这个时候Processor线程启动
1.2.2、Processor如何接收请求
在Processor的run()方法,
-
configureNewConnections()
,
Register any new connections that have been queued up
位置1:不断轮询,获取队列里面的SocketChannel
位置2:获取配置信息,产生ConnectionId
位置3:向Selector中注册这个SocketChannel,这里的Selector是kafka自己封装的一个KSelector,和Acceptor中的Selector不是一个
import org.apache.kafka.common.network.{ChannelBuilders, KafkaChannel, LoginType, Mode, Selector => KSelector}
(对生产者代码封装的Selector进行复用)
-
向KSelector中注册一个OP_READ的事件,这个时候就可以读取来自客户端发送过来的请求;此外,封装了一个KafkaChannel,放入到
-
poll()
:根据之前对Producer源码剖析,读取和发送数据的底代码都是在这个方法里面完成,具体剖析过程略;将数据放入stagedReceives
(尚未处理的数据)
1.2.3、Processor如何处理stagedReceives的请求
在Processor的run()方法,
processCompletedReceives()
,处理接收标记完成的数据,
位置1:遍历每一个请求
位置2:对于获取到的请求按照协议进行解析,解析出来就是一个个Request
位置3:本质上调用requestQueue.put(request)
,把这个请求放入RequestQueue
通过参数queued.max.requests
设置,RequestChannel在SocketServer初始化的时候创建
位置4:底层代码,移除OP_READ
事件,因为接下来要写数据
1.2.4、队列里面的Request什么时候被处理?
-
KafkaRequestHandlerPool
处理请求队列里面的请求