从上一节可以知道,接收新连接的处理是在NioEventLoop
的run()
方法中,里面有个方法叫做processSelectedKey()
,里面对于四种事件都有相应的判断并交给Channel
的Unsafe
属性来处理。
processSelectedKeys()
public final class NioEventLoop extends SingleThreadEventLoop {
private void processSelectedKeys() {
if (selectedKeys != null) {
// 监听到事件, selectedKeys肯定不为null
processSelectedKeysOptimized();
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
private void processSelectedKeysOptimized() {
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
// null out entry in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.keys[i] = null;
// 取出SelectionKey中的附件
// Java原生的java.nio.channels.SocketChannel把NioSocketChannel作为附件注册到Selector
// 所以这里取出来的就是Netty中的NioSocketChannel
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
// 走的是这里
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
if (needsToSelectAgain) {
// null out entries in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.reset(i + 1);
selectAgain();
i = -1;
}
}
}
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
// 这里取出来的 NioUnsafe 是 io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
// 这里是一些异常校验, 省略......
}
try {
// 读取事件类型
int readyOps = k.readyOps();
// 如果是 CONNECT 事件
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
// remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
// See https://github.com/netty/netty/issues/924
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
// 如果是 WRITE 事件
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
// Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
ch.unsafe().forceFlush();
}
// 如果是 READ 事件或者 ACCEPT 事件
// ACCEPT: 有新连接
// READ: 有可读数据
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
}
NioByteUnsafe#read()
来到NioByteUnsafe
的read
方法
public abstract class AbstractNioByteChannel extends AbstractNioChannel {
protected class NioByteUnsafe extends AbstractNioUnsafe {
@Override
public final void read() {
final ChannelConfig config = config();
if (shouldBreakReadReady(config)) {
clearReadPending();
return;
}
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
byteBuf = allocHandle.allocate(allocator);
// ② 读取数据到ByteBuf中
allocHandle.lastBytesRead(doReadBytes(byteBuf));
if (allocHandle.lastBytesRead() <= 0) {
// nothing was read. release the buffer.
byteBuf.release();
byteBuf = null;
close = allocHandle.lastBytesRead() < 0;
if (close) {
// There is nothing left to read as we received an EOF.
readPending = false;
}
break;
}
allocHandle.incMessagesRead(1);
readPending = false;
// ③ 触发channelRead()
// 数据在ChannelPipeline中传递
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
} while (allocHandle.continueReading());
allocHandle.readComplete();
// ④ 触发channelReadComplete()
pipeline.fireChannelReadComplete();
if (close) {
closeOnRead(pipeline);
}
} catch (Throwable t) {
handleReadException(pipeline, byteBuf, t, close, allocHandle);
} finally {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
}
① 通过allocHandle创建一个ByteBuf
,这里默认创建的是io.netty.buffer.PooledUnsafeDirectByteBuf
。
② 读取数据到ByteBuf
中,ByteBuf
内部其实也是对java.nio.ByteBuffer
的封装,底层也是从java.nio.channels.SocketChannel
读取数据到ByteBuffer
。
③ 触发ChannelPipeline
中的ChannelHandler
的channelRead()
方法,即读取到的数据会在ChannelPipeline
中传播。
④ 触发ChannelPipeline
中的ChannelHandler
的channelReadComplete()
方法。
doReadBytes(byteBuf)
看看doReadBytes(byteBuf)
内部是如何把数据读到ByteBuf
中
public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
@Override
protected int doReadBytes(ByteBuf byteBuf) throws Exception {
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
// 设置可读取的长度
allocHandle.attemptedBytesRead(byteBuf.writableBytes());
// 调用ByteBuf的writeBytes()方法, 写入数据到ByteBuf
// 第一个参数是Java原生的java.nio.channels.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);
// 第一个参数是写入的位置 第二参数是SocketChannel 第三个参数可写入的长度
int writtenBytes = setBytes(writerIndex, in, length);
if (writtenBytes > 0) {
writerIndex += writtenBytes;
}
return writtenBytes;
}
}
abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
ByteBuffer tmpNioBuf;
@Override
public final int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
try {
// 调用Java原生SocketChannel的read()方法
// read()方法的参数是Java原生的ByteBuffer
return in.read(internalNioBuffer(index, length));
} catch (ClosedChannelException ignored) {
return -1;
}
}
@Override
public final ByteBuffer internalNioBuffer(int index, int length) {
checkIndex(index, length);
return _internalNioBuffer(index, length, false);
}
final ByteBuffer _internalNioBuffer(int index, int length, boolean duplicate) {
index = idx(index);
// 这里的duplicate为false
ByteBuffer buffer = duplicate ? newInternalNioBuffer(memory) : internalNioBuffer();
buffer.limit(index + length).position(index);
return buffer;
}
protected final ByteBuffer internalNioBuffer() {
ByteBuffer tmpNioBuf = this.tmpNioBuf;
if (tmpNioBuf == null) {
this.tmpNioBuf = tmpNioBuf = newInternalNioBuffer(memory);
} else {
tmpNioBuf.clear();
}
return tmpNioBuf;
}
}
最后返回的实际上是this.tmpNioBuf
,它的类型的是java.nio.ByteBuffer
。
因此,Netty
的ByteBuf
底层也是包装了Java
原生的ByteBuffer
。
pipeline.fireChannelRead(byteBuf)
接着看数据如何在ChannelPipeline
中传播,查看pipeline.fireChannelRead(byteBuf)
内部
public class DefaultChannelPipeline implements ChannelPipeline {
// ①
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
// 从HeadContext开始调用
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
}
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
// ②
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
// 这里返回的还是msg
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
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) {
if (invokeHandler()) {
try {
// 调用里面的ChannelHandler的channelRead()方法
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
invokeExceptionCaught(t);
}
} else {
fireChannelRead(msg);
}
}
}
public class DefaultChannelPipeline implements ChannelPipeline {
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
// ④
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 调用ChannelHandlerContext的fireChannelRead()方法
// 触发下一个Context中Handler的调用
ctx.fireChannelRead(msg);
}
}
}
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
// ⑤
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
// findContextInbound是寻找下一个ChannelInboundHandler
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
// 可以发现, 这里又回到了上面的第②步
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);
}
});
}
}
}
从上面①②③④⑤可以知道,对于入站消息:
-
ChannelPipeline
是从HeadContext
开始执行的 -
同一个
Channel
的所有ChannelHandler
的执行都会放在EventLoop
中执行,所以它们是线程安全的 -
调用
ctx.fireChannelRead(msg)
即可触发下一个ChannelHandler
的执行
对于我们的EchoServer
程序,它里面有四个Handler
,
即HeadContext<=>LoggineHandler<=>EchoServerHandler<=>TailContext
,EchoServerHandler
中channelRead()
方法为:
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 读取数据后写回客户端
ctx.write(msg);
// if (msg instanceof ByteBuf) {
// ByteBuf buf = (ByteBuf) msg;
// System.out.println(ctx.channel() + "--->" + buf.toString(CharsetUtil.UTF_8));
// }
}
}
这里并没有调用ctx.fireChannelRead(msg)
,所以流程并不会走到TailContext
。
HeadContext
,不仅是一个ChannelHandlerContext
,也是一个ChannelInboundHandler
,同时也是一个ChannelOutboundHandler
。
TailContext
,不仅是一个ChannelHandlerContext
,同时也是一个ChannelInboundHandler
。
看看TailContext
的channelRead(ChannelHandlerContext ctx, Object msg)
方法做了什么:
public class DefaultChannelPipeline implements ChannelPipeline {
final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
onUnhandledInboundMessage(ctx, msg);
}
}
protected void onUnhandledInboundMessage(ChannelHandlerContext ctx, Object msg) {
onUnhandledInboundMessage(msg);
if (logger.isDebugEnabled()) {
logger.debug("Discarded message pipeline : {}. Channel : {}.",
ctx.pipeline().names(), ctx.channel());
}
}
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 {
ReferenceCountUtil.release(msg);
}
}
}
可以看到,最后一行释放了ByteBuf
的引用,对于池化的ByteBuf
,可以让它们重新回到池中,对于非池化的ByteBuf
,可以释放它们占用的内存。
总结
当有新的数据可以读取时:
- 创建一个
ByteBuf
,ByteBuf
内部是对Java
原生的ByteBuffer
的封装 - 调用
Java
原生SocketChannel
的read()
方法,将数据读取到ByteBuf
内部的ByteBuffer
- 调用
pipeline.fireChannelRead(byteBuf)
,这里会依次调用pipeline中的channelRead(ChannelHandlerContext ctx, Object msg)
方法 - 调用
pipeline.fireChannelReadComplete()
,这里会依次调用pipeline中的channelReadComplete(ChannelHandlerContext ctx)