目录
writeAndFlush
Netty写出数据是通过Outbound事件传播完成的,具体则是通过pipeline.writeAndFlush从tail节点开始依次向前传播,最后由head节点完成数据的写出与刷新。
writeAndFlush的传播过程:
- 从tail节点开始向前传播。
- 逐个调用channelHandler的write方法。
- 逐个调用channelHandler的flush方法。
MessageToByteEncoder
MessageToByteEncoder实现了ChannelOutboundHandlerAdapter接口,是Netty编码的最顶层抽象类,它将编码过程进行整体抽象,具体编码细节交给子类实现,通过pipeline传播Outbound事件。
整体过程可以分为以下六步:
- 数据匹配校验。
- 分配内存。
- 调用子类的编码实现。
- 释放原始对象。
- 传播ByteBuf直到head节点写出。
- 释放内存。
public abstract class MessageToByteEncoder<I> extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
ByteBuf buf = null;
try {
// 1.判断当前handler是否能处理这个数据对象
if (acceptOutboundMessage(msg)) {
@SuppressWarnings("unchecked")
I cast = (I) msg;
// 2.分配内存(优先分配direct内存)
buf = allocateBuffer(ctx, cast, preferDirect);
try {
// 3.进行具体编码操作,将编码后的对象填充到ByteBuf(由子类实现)
encode(ctx, cast, buf);
} finally {
// 4.释放原始对象
ReferenceCountUtil.release(cast);
}
if (buf.isReadable()) {
// 5.传播ByteBuf,直到head节点
ctx.write(buf, promise);
} else {
buf.release();
ctx.write(Unpooled.EMPTY_BUFFER, promise);
}
buf = null;
} else {
ctx.write(msg, promise);
}
} catch (EncoderException e) {
throw e;
} catch (Throwable e) {
throw new EncoderException(e);
} finally {
// 如果出现异常,会释放资源
if (buf != null) {
buf.release();
}
}
}
}
head节点的write方法总结
- 将heap内存转换成direct内存。
- 将要写出的数据加入到写缓冲区。
- 如果缓冲区超过一定容量,设置写状态为不可写,并将写状态事件传播给handler。
head节点的flush方法总结
- 添加刷新标志并更新写状态。
- 遍历Buffer队列,过滤ByteBuf。
- 调用JDK底层API进行自旋写。