【JAVA 网络编程系列】Netty -- Netty 接受新数据
【1】主循环中处理 Read 事件
public final class NioEventLoop extends SingleThreadEventLoop {
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
...
try {
// 检索此键的就绪操作集
int readyOps = k.readyOps();
...
// 处理 Read / Accept 事件
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
// 处理异常
unsafe.close(unsafe.voidPromise());
}
}
}
【2】Read 事件的处理流程
【2.1】处理接受新数据请求时的 unsafe 继承结构
针对 NioSocketChannel 其 unsafe 对应的是 NioSocketChannelUnsafe 类;
【2.2】数据读取流程
public abstract class AbstractNioByteChannel extends AbstractNioChannel {
protected class NioByteUnsafe extends AbstractNioUnsafe {
@Override
public final void read() {
// 获取 ServerSocketChannel 中的 ChannelConfig 实例
final ChannelConfig config = config();
// 获取 ServerSocketChannel 中的 ChannelPipeline 实例
final ChannelPipeline pipeline = pipeline();
// 初始化内存分配器
// ByteBuf 的分配器
final ByteBufAllocator allocator = config.getAllocator();
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
// 重置已累积的所有计数器,并建议下一个读取循环应读取多少消息/字节
allocHandle.reset(config);
ByteBuf byteBuf = null;
boolean close = false;
try {
do {
// 通过allocator创建一个ByteBuf
// 默认地,通过各种参数判断当前操作系统是否允许池化、Unsafe、堆外这三个指标
// 当然,这些参数也可以通过启动参数来控制
byteBuf = allocHandle.allocate(allocator);
// 读取数据到ByteBuf中
// lastBytesRead
// Set the bytes that have been read for the last read operation
// 设置上次读取操作已读取的字节
// doReadBytes 方法为
// protected abstract int doReadBytes(ByteBuf buf) throws Exception; 由子类覆写
allocHandle.lastBytesRead(doReadBytes(byteBuf));
// lastBytesRead
// Get the amount of bytes for the previous read operation.
// 获取上一次读取操作的字节数
if (allocHandle.lastBytesRead() <= 0) {
// 未读取到数据释放ByteBuf
// nothing was read. release the buffer.
byteBuf.release();
byteBuf = null;
// 标记 close 标志
close = allocHandle.lastBytesRead() < 0;
if (close) {
readPending = false;
}
break;
}
// incMessagesRead
// Increment the number of messages that have been read for the current read loop
// 增加当前读取循环已读取的消息数
allocHandle.incMessagesRead(1);
readPending = false;
// 触发 ChannelHandler 的 channelRead() 方法
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
// continueReading
// Determine if the current read loop should should continue
// 确定当前读取循环是否应继续
} while (allocHandle.continueReading());
// The read has completed
// 标记读取已完成
allocHandle.readComplete();
// 触发 ChannelHandler 的 channelReadComplete() 方法
pipeline.fireChannelReadComplete();
// 数据接受完毕
if (close) {
// 关闭 Channel
closeOnRead(pipeline);
}
} catch (Throwable t) {
// 处理异常
handleReadException(pipeline, byteBuf, t, close, allocHandle);
} finally {
// 当没有未读信息并且Channel不是AutoRead的
if (!readPending && !config.isAutoRead()) {
// 移除与读相关的兴趣事件集
removeReadOp();
}
}
}
}
}
public class NioSocketChannel extends AbstractNioByteChannel
implements io.netty.channel.socket.SocketChannel {
@Override
protected int doReadBytes(ByteBuf byteBuf) throws Exception {
// 分配接受处理相关的Handle
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
// 设置可读取的长度
allocHandle.attemptedBytesRead(byteBuf.writableBytes());
// 调用ByteBuf的writeBytes()方法
// 第一个参数是Java原生的SocketChannel,第二个参数是可读取的长度
return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());
}
}
public abstract class AbstractByteBuf extends ByteBuf {
@Override
public int writeBytes(ScatteringByteChannel in, int length) throws IOException {
// 保证容量足够,里面有扩容的逻辑
ensureWritable(length);
// 调用setBytes()方法
// 第一个参数是写入的位置,第二参数是SocketChannel,第三个参数可写入的长度
int writtenBytes = setBytes(writerIndex, in, length);
if (writtenBytes > 0) {
writerIndex += writtenBytes;
}
return writtenBytes;
}
}
【2.3】数据在 Pipeline 中的传递过程
Pipeline 中传递数据流程图示
public class DefaultChannelPipeline implements ChannelPipeline {
protected void onUnhandledInboundMessage(Object msg) {
try {
// 记录日志
logger.debug(
"Discarded inbound message {} that reached at the tail of the pipeline. " +
"Please check your pipeline configuration.", msg);
} finally {
// 释放ByteBuf的引用
ReferenceCountUtil.release(msg);
}
}
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
// 从HeadContext开始调用
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 调用ChannelHandlerContext的fireChannelRead()方法
// 触发下一个Context中Handler的调用
ctx.fireChannelRead(msg);
}
}
final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 数据传递到TailContext则打印日志并释放ByteBuf
// 目的
// 1.实现 ChannelPipeline 的完整性,有头有尾
// 2.回收资源
onUnhandledInboundMessage(msg);
}
}
}
abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
implements ChannelHandlerContext, ResourceLeakHint {
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
// 创建信息数据
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
// 获取Channel的执行器
EventExecutor executor = next.executor();
// 判断当前线程是不是在EventLoop中
if (executor.inEventLoop()) {
next.invokeChannelRead(m);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
private void invokeChannelRead(Object msg) {
// invokeHandler() 确定是否已经调用过
// ChannelHandler#handlerAdded(ChannelHandlerContext)
if (invokeHandler()) {
try {
// 调用里面的ChannelHandler的channelRead()方法
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
// 异常处理
notifyHandlerException(t);
}
} else {
// 从Pipeline中确定一个inbound的HandlerContext
// 并从该HandlerContext发开触发ChannelHandler的channelRead()方法
fireChannelRead(msg);
}
}
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
// 寻找下一个可用的ChannelHandlerContext
// 从Pipeline中确定一个inbound的HandlerContext
// 并从该HandlerContext发开触发ChannelHandler的channelRead()方法
invokeChannelRead(findContextInbound(), msg);
return this;
}
private AbstractChannelHandlerContext findContextInbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while (!ctx.inbound);
return ctx;
}
}
@Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 此处读取数据后,写回客户端
// 没有调用ctx.fireChannelRead(msg);
// 此时数据不会继续向下一个ChannelHandlerContext传递
ctx.write(msg);
}
}
参考致谢
本博客为博主学习笔记,同时参考了网上众博主的博文以及相关专业书籍,在此表示感谢,本文若存在不足之处,请批评指正。
【1】慕课专栏,网络编程之Netty一站式精讲
【2】极客时间,Netty源码剖析与实战