1.KafkaChannel简述
KafkaChannel是对SocketChannel的封装,是Kafka中负责网络读写的最底层类。封装了SocketChannel,还封装了Kafka自己的认证器Authenticator。屏蔽Kafka的上层逻辑,来看KafkaChannel是如何设计的。
KafkaChannel网络读写模型:
Kafka-client是nio selector模型的网络通信,自然KafkaChannel读写也是面向缓冲区的。KafkaChannel的写之前会向内存池开辟堆外内存,作为写缓冲区,缓冲区承载数据,缓冲区的结构是头部使用4个byte的int类型表示该缓冲区长度,头部后面为写入的实际数据。写的时候,将写缓冲区写入与指定node建立的SocketChannel。
读的之前,同样先回申请开辟一个读缓冲区,然后SocketChanne将读取数据到读缓冲区。
问题:为什么缓冲区需要一个size头部?
为了解决TCP粘包拆包问题。粘包/拆包问题是TCP编程必然解决的问题,TCP这种底层流协议。他根本无法感知上层业务数据边界,所以需要进行粘包拆包设计。主要还是采取使用一个size来表示消息长度,这样在读取的时候,一旦读取到消息的长度,就可以知道应该读取多长的流,就能解决粘包问题。当读取长度不够size,就持续接受。就解决了拆包问题。在一些TCP框架里面还会扩充一些其他分割器方案,比如netty里面提供了定长分割器和分隔符分割器。在Kafka里面,在是4个字节的来表示消息长度,使用的int变量。TCP不是本文分析的重点,就点到为止。下面细细分析KafkaChannel
2:KafkaChannel承担了哪些事情?
通过主要method来看看KafkaChannel有哪些功能承担了哪些事情?
Method | 作用 |
---|---|
void prepare() | 握手认证 |
KafkaPrincipal principal() | 获取认证信息 |
boolean finishConnect() | 是否建立连接完成 |
boolean isConnected() | 是否建立连接 |
SelectionKey selectionKey() | 获取该channel上的SelectionKey |
void setSend(Send send) | 初始化需要发送的数据 |
NetworkReceive read() | 网络读(读取Kafka服务器数据) |
Send write() | 网络写(向Kafka服务器发送数据) |
void disconnect() | 断开连接 |
void state(ChannelState state) | 设置该连接的连接状态 |
ChannelState state() | 获取该连接的连接状态 |
3.KafkaChannel网络读写的源码分析
3.1setSend(Send send)源码分析
public void setSend(Send send) {
if (this.send != null)
throw new IllegalStateException("Attempt to begin a send operation with prior send operation still in progress, connection id is " + id);
this.send = send;
//添加到interestOps [见下]
this.transportLayer.addInterestOps(SelectionKey.OP_WRITE);
}
public void addInterestOps(int ops) {
//SelectionKey
key.interestOps(key.interestOps() | ops);
}
setSend(Send send)就是将需要发送的数据设置到KafkaChannel的send成员中。(是在Kafka的selector的send方法中。关于kafka的selector,还会专门分析)然后给让selector增加写监控事件。Send 就是需要要发送的数据。
主要是来分析Send 是如何构建的。Send 是最上层接口。producer发送消息时候采用的是子类NetworkSend
3.2NetworkSend的构建
public class NetworkSend extends ByteBufferSend {
public NetworkSend(String destination, ByteBuffer buffer) {
//见下段分析
super(destination, sizeDelimit(buffer));
}
/**
* 组装ByteBuffer[size|data]的结构
* @param buffer
* @return
*/
private static ByteBuffer[] sizeDelimit(ByteBuffer buffer) {