ChannelOutboundBuffer介绍
ChannelOutboundBuffer是Netty发送缓存,当Netty调用write时数据不会真正的去发送而是写入到ChannelOutboundBuffer缓存队列,直到调用flush方法Netty才会从ChannelOutboundBuffer取数据发送。每个Unsafe都会绑定一个ChannelOutboundBuffer,也就是说每个客户端连接上服务端都会创建一个ChannelOutboundBuffer绑定客户端Channel。Netty设计ChannelOutboundBuffer是为了减少TCP缓存的压力提高系统的吞吐率。
ChannelOutboundBuffer设计
先来看下ChannelOutboundBuffer的4个重要字段
private Entry flushedEntry; 待发送数据起始节点
private Entry unflushedEntry;暂存数据起始节点
private Entry tailEntry;尾节点
private int flushed;待发送数据个数
Entry(flushedEntry) -->Entry--> ... Entry--> Entry(unflushedEntry) -->Entry ... Entry--> Entry(tailEntry)
flushedEntry(包括)到unflushedEntry之间的就是待发送数据,unflushedEntry(包括)到tailEntry就是暂存数据,flushed就是待发送数据个数。
正常情况下待发送数据发送完成后会flushedEntry指向unflushedEntry位置,并将unflushedEntry指空变成如下情况:
Entry(flushedEntry) -->Entry ... Entry--> Entry(tailEntry)
但是如果出现TCP缓存满的导致的半包情况,flushedEntry不会向后移动或移动发送成功的个数个位置,例如发送成功了一个数据,就会向前移动一个位置,出现如下情况:
Entry(flushedEntry) -->... Entry--> Entry(unflushedEntry) -->Entry ... Entry--> Entry(tailEntry)
下面介绍ChannelOutboundBuffer中几个主要的方法
-
addMessage方法,功能是添加数据到队列的队尾。
-
addFlush方法,准备待发送的数据,在flush前需要调用。
-
nioBuffers方法,获取待发送数据,发送数据的时候需要调用拿数据。
-
removeBytes方法,发送完成后需要调用删除已经写入TCP缓存成功的数据。
下面对几个方法源码进行分析
addMessage方法源码分析
addMessage方法是在系统调用write方法的时候调用
public void addMessage(Object msg, int size, ChannelPromise promise) {
//将消息数据包装成Entry对象
Entry entry = Entry.newInstance(msg, size, total(msg), promise);
//队列为空的情况
if (tailEntry == null) {
flushedEntry = null;
tailEntry = entry;
//非空情况,将新节点放尾部
} else {
Entry tail = tailEntry;
tail.next = entry;
tailEntry = entry;