前面几篇博客我已经介绍整个服务端的启动的过程,主要就是服务端的Reactor
线程的启动流程,一些信息的初始化过程,这篇博客我主要介绍客户端的接入的过程,主要还是看服务端的Reactor
的线程中干的事,主要的代码如下,我们继续重温一下,具体的代码如下:
public final class NioEventLoop extends SingleThreadEventLoop {
//事件循环
@Override
protected void run() {
for (;;) {
try {
try {
//hasTasks() 若taskQueue or tailTasks任务队列中有任务 返回true 没有则返回false
//有任务返回selectNow的返回值 没任务返回-1
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.BUSY_WAIT:
// fall-through to SELECT since the busy-wait is not supported with NIO
case SelectStrategy.SELECT:
//首先轮询注册到reactor线程对应的selector上的所有的channel的IO事件
//wakenUp 表示是否应该唤醒正在阻塞的select操作,netty在每次进行新的loop之前,都会将wakeUp 被设置成false,标志新的一轮loop的开始
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
// fall through
default:
}
} catch (IOException e) {
// If we receive an IOException here its because the Selector is messed up. Let's rebuild
// the selector and retry. https://github.com/netty/netty/issues/8566
rebuildSelector0();
handleLoopException(e);
continue;
}
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
//2.处理产生网络IO事件的channel
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
//3.处理任务队列
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
}
客户端的接入的代码,主要在processSelectedKeys();
方法中,我们继续跟进去看看对应的代码,具体的代码如下:
public final class NioEventLoop extends SingleThreadEventLoop {
private void processSelectedKeys() {
if (selectedKeys != null) {
processSelectedKeysOptimized();
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
}
上面的代码能进,这个时候我们需要了解客户端的接入的过程,所以这儿应该selectedKeys
不为空,所以会进入processSelectedKeysOptimized();
方法中去。但是最上面的Reactor
线程是一个死循环,所以有时候客户端没有连接的服务端,这个方法还是有可能进的,这个时候selectedKeys
的就是为空,所以会进入下面的方法processSelectedKeysPlain(selector.selectedKeys());
当我们打开对应的方法的时候发现和processSelectedKeysOptimized();
方法差不多,无非多了一个判断。所以在这我们只需要跟进processSelectedKeysOptimized();
方法即可,具体的代码如下:
public final class NioEventLoop extends SingleThreadEventLoop {
private void processSelectedKeysOptimized() {
//遍历selectedKeys
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;
//获取对应的附加对象
final Object a = k.attachment();
//由于我们前面添加的附加对象就是AbstractNioChannel
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;
}
}
}
}
上面的代码遍历selectedKeys
,先获取对应的selectedKeys
上的附加对象,由于我们之前添加的附加对象就是AbstractNioChannel
,所以这儿的判断是直接会进的,我们直接跟进对应的方法processSelectedKey(k, (AbstractNioChannel) a);
具体的代码如下:
public final class NioEventLoop extends SingleThreadEventLoop {
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
// k.isValid()告知此键是否有效。
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
return;
}
if (eventLoop != this || eventLoop == null) {
return;
}
// close the channel if the key is not valid anymore
unsafe.close(unsafe.voidPromise());
return;
}
try {
//获取此键的 ready 操作集合。
int readyOps = k.readyOps();
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
ch.unsafe().forceFlush();
}
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
}
上面走来会判断这个键是否有效,如果有效直接执行k.readyOps();
获取已经准备好的事件,这个判断是什么事件,由于服务端我们只注册了OP_ACCEPT
事件,所以这儿我们会进unsafe.read();
方法,具体的代码如下:
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
assert eventLoop().inEventLoop();
//获取配置的信息
final ChannelConfig config = config();
//获取对应的pipeline
final ChannelPipeline pipeline = pipeline();
//类似ByteBuf
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
//设置对应的配置
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
//这个时候会执行这个方法
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
if (exception != null) {
closed = closeOnReadError(exception);
pipeline.fireExceptionCaught(exception);
}
if (closed) {
inputShutdown = true;
if (isOpen()) {
close(voidPromise());
}
}
} finally {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
}
上面的代码会先来获取对应的配置的信息,以及对应的pipeline
的信息,然后将获取到的配置信息设置到ByteBuf
,最后会执行doReadMessages(readBuf);
方法,我们继续跟进对应的代码
public class NioServerSocketChannel extends AbstractNioMessageChannel
implements io.netty.channel.socket.ServerSocketChannel {
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
//调用accept方法,同意连接
SocketChannel ch = SocketUtils.accept(javaChannel());
try {
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable t) {
logger.warn("Failed to create a new channel from an accepted socket.", t);
try {
ch.close();
} catch (Throwable t2) {
logger.warn("Failed to close a socket.", t2);
}
}
return 0;
}
}
上面的代码会调用底层的accept
的方法,这个获取的是客户端的socket
,这个时候将客户端的socket
封装成一个NioSocketChannel
,当我们打开对应的代码,发现和我们之前创建NioServerSocketChannel
差不多,这儿就不细说,可以看我之前写的博客,唯一不同的应该就是Unsafe
的值不同了。这时候创建的Unsafe
是NioByteUnsafe
,其他都差不多,创建的pipeline
也是一样呢的,头节点是HeadContext
,尾节点是TailContext
。这个时候会将这个创建好的NioSocketChannel
添加对应传进来的List
集合中去。最后返回的1,我们再回到原来对应的代码地方,具体的代码如下:
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
//省略一部分代码
Throwable exception = null;
try {
try {
do {
//这个时候会执行这个方法
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
if (exception != null) {
closed = closeOnReadError(exception);
pipeline.fireExceptionCaught(exception);
}
if (closed) {
inputShutdown = true;
if (isOpen()) {
close(voidPromise());
}
}
} finally {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
}
这个时候假设我们对应的事件遍历完成后,这个循环就会结束,这个时候会遍历原来添加了NioSocketChannel
的集合,这个时候由于有客户端连接进来了,所以这个集合不会为空,这个时候会执行pipeline.fireChannelRead(readBuf.get(i));
方法,记住这儿获取的是服务端的pipeline
,这个方法是向下传播,我们跟进去看看
public class DefaultChannelPipeline implements ChannelPipeline {
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
//在新连接接入时 msg是NioSocketChannel
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
}
这个方法熟悉不,就是去执行头节点的channelRead
方法,我们继续跟进对应invokeChannelRead(head, msg);
方法,具体的代码如下:
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
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);
}
});
}
}
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRead(msg);
}
}
}
这个时候会真正的去执行channelRead(this, msg);
方法,我们跟进去看看对应的代码,具体的代码如下:
public class DefaultChannelPipeline implements ChannelPipeline {
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println(this.getClass() + " HeadContext.channelRead");
ctx.fireChannelRead(msg);
}
}
}
这个时候又执行一波向下传播,其实代码和服务端的都是差不多,我们这儿假设我们之前没有往服务端中添加新的pipeline
,这个时候就会调用ServerBootstrapAcceptor
类中channelRead
方法,具体的代码如下
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
//将我们之前传入的pipeline设置到NioSocketChannel中去。
child.pipeline().addLast(childHandler);
//将options的参数也设置进去
setChannelOptions(child, childOptions, logger);
//将attr属性也设置进去
for (Entry<AttributeKey<?>, Object> e : childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
try {
//workGroup
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
}
}
上面将传进来的NioSocketChannel
类设置好对应的pipeline
和options
以及对应的attr
,然后调用客户端的NioEventLoop
的register
的方法,我们继续跟进对应的方法,当我们打开对应的代码发现,和服务端的执行的流程是一样的,这个时候不得不感概Netty
源码写的是真的好。这儿经过一连串的操作,创建了一个客户端的Reactor
线程,服务端的感兴趣的事件注册是在调用doBind0
方法的时候绑定的,那么这儿是在那绑定的?我们回到原来执行的方法的地方,继续跟进对应的代码,具体的代码如下:
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
//省略一部分代码
Throwable exception = null;
try {
//省略一部分代码
readBuf.clear();
allocHandle.readComplete();
//执行服务端pipeline中ChannelReadComplete方法
pipeline.fireChannelReadComplete();
if (exception != null) {
closed = closeOnReadError(exception);
pipeline.fireExceptionCaught(exception);
}
if (closed) {
inputShutdown = true;
if (isOpen()) {
close(voidPromise());
}
}
} finally {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
}
到此整个主Reactor
线程就结束,但是我们工作Reactor
的线程已经启动,但是没有注册到感兴趣的事件,这个时候需要我们继续看代码,由于调用的代码都是一样,所以前面我就直接不细说了,我们查看pipeline.fireChannelReadComplete();
方法,注意这儿是工作的Reactor线程调用的方法。我们继续跟进对应的代码,具体的代码如下:
public class DefaultChannelPipeline implements ChannelPipeline {
@Override
public final ChannelPipeline fireChannelReadComplete() {
AbstractChannelHandlerContext.invokeChannelReadComplete(head);
return this;
}
}
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
static void invokeChannelReadComplete(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelReadComplete();
} else {
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(tasks.invokeChannelReadCompleteTask);
}
}
private void invokeChannelReadComplete() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelReadComplete(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelReadComplete();
}
}
}
public class DefaultChannelPipeline implements ChannelPipeline {
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
System.out.println(this.getClass() + " HeadContext.channelReadComplete");
ctx.fireChannelReadComplete();
//调用对应的添加到感兴趣的事件的方法
readIfIsAutoRead();
}
}
}
上面的代码和服务端是一样的,都是经过一连串的调用,调用每个pipeline
中channelReadComplete();
我们要找到的是添加感兴趣的事件,对应添加感兴趣的事件就是readIfIsAutoRead();
我们继续跟进对应的代码,具体的代码如下:
public class DefaultChannelPipeline implements ChannelPipeline {
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
}
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
@Override
public Channel read() {
pipeline.read();
return this;
}
}
public class DefaultChannelPipeline implements ChannelPipeline {
@Override
public final ChannelPipeline read() {
tail.read();
return this;
}
}
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
@Override
public ChannelHandlerContext read() {
final AbstractChannelHandlerContext next = findContextOutbound(MASK_READ);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeRead();
} else {
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(tasks.invokeReadTask);
}
return this;
}
private void invokeRead() {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).read(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
read();
}
}
}
public class DefaultChannelPipeline implements ChannelPipeline {
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
@Override
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
}
}
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
@Override
public final void beginRead() {
assertEventLoop();
if (!isActive()) {
return;
}
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
}
public abstract class AbstractNioChannel extends AbstractChannel {
@Override
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
}
上面的代码经过一系列的调用终于调用到注册感兴趣的事件了。至此整个Netty
的工作流程就讲完了,下篇博客我会介绍一下Netty
中内置的pipeline
,最后总结一下整个Netty
的工作流程。具体如下图: