参考资料
触发读事件
//NioEventLoop的run方法
//这个是事件轮训线程的轮训方法,会轮训处理相应的事件,比如读事件
protected void run() {
for (;;) {
try {
//获取到Selector上的io事件
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
}
轮训读取通道数据,触发处理器链
//NioByteUnsafe的read方法
//通过channel的unsafe去读取数据
@Override
public final void read() {
//省去不需要代码
// 1.轮训读取通道数据
// 2.将读取到的数据放到处理器链中处理
try {
do {
byteBuf = allocHandle.allocate(allocator);
allocHandle.lastBytesRead(doReadBytes(byteBuf));
if (allocHandle.lastBytesRead() <= 0) {
// nothing was read. release the buffer.
byteBuf.release();
byteBuf = null;
close = allocHandle.lastBytesRead() < 0;
break;
}
allocHandle.incMessagesRead(1);
readPending = false;
//读取到数据,通过处理器链进行部分数据处理
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
} while (allocHandle.continueReading());
//省去不需要代码
}
处理器链执行流程
//DefaultChannelPipeline的fireChannelRead方法
//pipeline的一个触发处理器链的方法,是处理器链入口,从头结点开始处理
public final ChannelPipeline fireChannelRead(Object msg) {
//触发处理器链,因为是读取,所以从头结点开始
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
//AbstractChannelHandlerContext的三个方法,是迭代处理器链关键的三个方法
//fireChannelRead方法用于迭代下一个处理器,根据findContextInbound方法
//我们在自定义的处理器中,在处理结尾也要触发当前处理器的这个方法,这样才能放行
//执行下一个处理器
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
invokeChannelRead(findContextInbound(), msg);
return this;
}
//invokeChannelRead方法用于一个新处理器的入口,会调用invokeChannelRead处理当前处理器逻辑
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRead(m);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
//invokeChannelRead方法用于真正回调当前处理器的实现方法
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRead(msg);
}
}
总结
1.轮训读取通道数据,每次都是部分数据,到处理器中的数据可能会有粘包和半包问题
2.处理器链非常强大,扩展性也很强,有利于实现不同的处理器的功能,对处理器进行分层实现,这样我们整个处理器链将会变得非常强大
3.这里只是分析了读方法,数据是由通道底层进入的,如果是写方法,那么数据是由我们传递到通道底层,这个顺序是相反的,相对应处理器链调用顺序也是一样