@Override
public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) { //@1
ByteBuf buffer;
if (cumulation.writerIndex() > cumulation.maxCapacity() - in.readableBytes() //@2
|| cumulation.refCnt() > 1) { //@3
// Expand cumulation (by replace it) when either there is not more room in the buffer
// or if the refCnt is greater then 1 which may happen when the user use slice().retain() or
// duplicate().retain().
//
// See:
// - https://github.com/netty/netty/issues/2327
// - https://github.com/netty/netty/issues/1764
buffer = expandCumulation(alloc, cumulation, in.readableBytes()); //@4
} else {
buffer = cumulation;
}
buffer.writeBytes(in); // @5
in.release(); //@6
return buffer;
}
};
代码@1:参数1:具体的内存分配器,参数2:已累计接收的自己缓冲,参数3:本次读取的字节缓冲区。
代码@2:首先检测当前的累积缓存区是否能够容纳新增加的ByteBuf,如果容量不够,则需要扩展ByteBuf,为了避免内存泄漏,手动去扩展。
代码@3:如果累积缓存区引用数超过1,也需要扩展。
代码@4:扩充累积缓存区。
代码@5:将新输入的字节写入到累积缓存区。
代码@6:释放缓存区。
代码@4:扩展缓冲区实现。
static ByteBuf expandCumulation(ByteBufAllocator alloc, ByteBuf cumulation, int readable) {
ByteBuf oldCumulation = cumulation;
cumulation = alloc.buffer(oldCumulation.readableBytes() + readable);
cumulation.writeBytes(oldCumulation);
oldCumulation.release();
return cumulation;
}
这里有个非常关键的点:每次扩展的时候,都是产生一个新的累积缓存区,这里主要是确保每一次通道读,所涉及的缓存区不是同一个,这样减少释放跟踪的难度,避免内存泄露。
2.2 相关事件处理
解码器 ByteToMessageDecoder 在 Netty 中属于 InBound(输入方向)。主要关注的事件包括 handlerRemoved、channelReader、channelReadComplete、channelInactive。
2.2.1 handlerRemoved事件
handler从ChannelPipeline中移除时调用。
@Override
public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
ByteBuf buf = internalBuffer();
int readable = buf.readableBytes();
if (readable > 0) {
ByteBuf bytes = buf.readBytes(readable);
buf.release();
ctx.fireChannelRead(bytes);
ctx.fireChannelReadComplete();
} else {
buf.release();
}
cumulation = null;
handlerRemoved0(ctx);
}
该方法主要的实现思路是,如果内部的累积缓存区可读,则需要将剩余的字节处理,然后释放内部累积缓存区,并设置为空,然后提供一个钩子函数,供子类去实现。handlerRemoved0(ctx)。
2.2.2 channelRead 通道读事件
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) { //@1
RecyclableArrayList out = RecyclableArrayList.newInstance(); //@2
try {
ByteBuf data = (ByteBuf) msg;
first = cumulation == null; //@3
if (first) {
cumulation = data;
} else {
cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data); //@4
}
callDecode(ctx, cumulation, out); //@5
} catch (DecoderException e) {
throw e;
} catch (Throwable t) {
throw new DecoderException(t);
} finally {
if (cumulation != null && !cumulation.isReadable()