上一篇文章记录了服务端和客户端的启动流程,本文介绍一下类关系以及eventLoop等知识,本研究主要还是以NIO为主
AbstractNioByteChannel - NioSocketChannel
Channel - AbstractChannel - AbstractNioChannel -
AbstactNioMessageChannel - NioServerSocketChannel
Unsafe(Channel) - AbstractUnsafe(AbstractChannel) - AbstractNioUnsafe(AbstractNioChannel )
NioByteUnsafe(AbstractNioByteChannel) - NioSocketChannelUnsafe(NioSocketChannel)
--
NioMessageUnsafe( AbstactNioMessageChannel )
Unsafe是Channle的内部接口,这么定义的目的是为了独立出unsafe的功能,unsafe是用于真正通信的组件,而unsafe又应该只被channel调用,而不应该被其他模块可见
再看一下ServerBootStrap中的两个group的含义以及目的,第一个group可以称为bossGroup,第二个group可以称为workerGroup,在Server启动后,bossGroup中一个eventLoop会启动进行轮询, NioServerSocketChannel在这个eventLoop的selector上注册了Accept事件,当有客户端连接上来后,会触发selectKey,
在eventLoop中处理selectKey的代码中
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
if (!ch.isOpen()) {
// Connection already closed - no need to handle write.
return;
}
}
执行到 NioMessageUnsafe的read方法,执行到NioServerSocketChannel的doReadMessages方法
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = javaChannel().accept();
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;
}
此时一个新的连接在服务端就会有一个新的NioSocketChannel对象,到这里为止,只是对象建立了,后续还有对象的pipeline,handler等等需要构建
在 NioMessageUnsafe完成 doReadMessages方法后,会触发 pipeline.fireChannelRead(readBuf.get(i)),由于这个pipeline是NioServerSocketChannel的pipeline
readBuf.get(i)的值就是前面新建的NioSocketChannel对象,根据handler链调用到前面一章介绍到的 ServerBootstrapAcceptor,执行到channelRead方法,该方法包括两个主要工作
1 child.pipeline().addLast(childHandler); 该步骤会将应用程序预先设置的的handler加到该NioSocketChannel的pipeline中
2 childGroup.register(child) ,其中childGroup就是用户设置的workGroup
从上面的解释可以很清楚的看到,处理客户端连接的是bossGroup,而新建NioSocketChannel对象后,这个对象会用workerGroup进行连接处理等
再来看看EventLoop,还是以NioEventLoop为例来介绍,NioEventLoop需要处理网络I/O读写事件,还需要执行系统任务和定时任务
在Netty的很多代码中,都会有如下实现方式:
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
首先会判断当前执行线程是否是eventLoop线程,如果不是会通过eventLoop的execute方法进行异步执行,再来看看execute方法
execute方法在SingleThreadEventExecutor
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
addTask(task);
} else {
startThread();
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
如果当前线程是eventLoop线程,则直接放到task队列中,如果不是,则尝试开始启动eventLoop线程并在内部缓存进行记录线程启动状态后放入task队列
eventLoop中有for的循环体,用来处理selectkey事件,eventLoop处理IO和系统任务的比例有ioRadio控制,eventLoop会保证先处理完IO事件,完成IO事件后,会根据IO事件的事件和配置的比例算出对应执行系统任务的时间,开始执行系统任务,执行64个任务后,比较任务总执行时间是否超过配置的时间,再进行对应的处理
// Check timeout every 64 tasks because nanoTime() is relatively expensive.
// XXX: Hard-coded value - will make it configurable if it is really a problem.
if ((runTasks & 0x3F) == 0) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
if (lastExecutionTime >= deadline) {
break;
}
最后来看看EventLoopGroup的关闭方法,shudownGraceFully的基本逻辑如下:
每个eventLoop中的都有内部变量STATE_UPDATE用来维护该eventLoop的状态,当执行shutdownGraceFully方法时,会将状态设置为shutdown,在这个步骤中仅仅是设置状态即可
在eventLoop的轮询中,完成IO轮询和任务调度后,会执行以下操作
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
break;
}
}
其中if的判断就是会去检查状态,如果是shutdown,则会执行closeAll,注意是group执行的shutodown,所以group中管理的所有eventLoop均会轮询到closeAll方法中
closeAll会循环eventLoop上的多路复用器selector上绑定的所有channel,通过channel的unsafe依次关闭channel,释放线程池,完成关闭操作
如果是用户调用的channel.close则会封装为用户任务,在eventLoop的轮询中进行处理
对于任何架构,线程模型设计的好坏都直接影响软件的性能和并发处理能力
AbstractNioByteChannel - NioSocketChannel
Channel - AbstractChannel - AbstractNioChannel -
AbstactNioMessageChannel - NioServerSocketChannel
Unsafe(Channel) - AbstractUnsafe(AbstractChannel) - AbstractNioUnsafe(AbstractNioChannel )
NioByteUnsafe(AbstractNioByteChannel) - NioSocketChannelUnsafe(NioSocketChannel)
--
NioMessageUnsafe( AbstactNioMessageChannel )
Unsafe是Channle的内部接口,这么定义的目的是为了独立出unsafe的功能,unsafe是用于真正通信的组件,而unsafe又应该只被channel调用,而不应该被其他模块可见
再看一下ServerBootStrap中的两个group的含义以及目的,第一个group可以称为bossGroup,第二个group可以称为workerGroup,在Server启动后,bossGroup中一个eventLoop会启动进行轮询, NioServerSocketChannel在这个eventLoop的selector上注册了Accept事件,当有客户端连接上来后,会触发selectKey,
在eventLoop中处理selectKey的代码中
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
if (!ch.isOpen()) {
// Connection already closed - no need to handle write.
return;
}
}
执行到 NioMessageUnsafe的read方法,执行到NioServerSocketChannel的doReadMessages方法
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = javaChannel().accept();
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;
}
此时一个新的连接在服务端就会有一个新的NioSocketChannel对象,到这里为止,只是对象建立了,后续还有对象的pipeline,handler等等需要构建
在 NioMessageUnsafe完成 doReadMessages方法后,会触发 pipeline.fireChannelRead(readBuf.get(i)),由于这个pipeline是NioServerSocketChannel的pipeline
readBuf.get(i)的值就是前面新建的NioSocketChannel对象,根据handler链调用到前面一章介绍到的 ServerBootstrapAcceptor,执行到channelRead方法,该方法包括两个主要工作
1 child.pipeline().addLast(childHandler); 该步骤会将应用程序预先设置的的handler加到该NioSocketChannel的pipeline中
2 childGroup.register(child) ,其中childGroup就是用户设置的workGroup
从上面的解释可以很清楚的看到,处理客户端连接的是bossGroup,而新建NioSocketChannel对象后,这个对象会用workerGroup进行连接处理等
再来看看EventLoop,还是以NioEventLoop为例来介绍,NioEventLoop需要处理网络I/O读写事件,还需要执行系统任务和定时任务
在Netty的很多代码中,都会有如下实现方式:
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
首先会判断当前执行线程是否是eventLoop线程,如果不是会通过eventLoop的execute方法进行异步执行,再来看看execute方法
execute方法在SingleThreadEventExecutor
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
addTask(task);
} else {
startThread();
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
如果当前线程是eventLoop线程,则直接放到task队列中,如果不是,则尝试开始启动eventLoop线程并在内部缓存进行记录线程启动状态后放入task队列
eventLoop中有for的循环体,用来处理selectkey事件,eventLoop处理IO和系统任务的比例有ioRadio控制,eventLoop会保证先处理完IO事件,完成IO事件后,会根据IO事件的事件和配置的比例算出对应执行系统任务的时间,开始执行系统任务,执行64个任务后,比较任务总执行时间是否超过配置的时间,再进行对应的处理
// Check timeout every 64 tasks because nanoTime() is relatively expensive.
// XXX: Hard-coded value - will make it configurable if it is really a problem.
if ((runTasks & 0x3F) == 0) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
if (lastExecutionTime >= deadline) {
break;
}
最后来看看EventLoopGroup的关闭方法,shudownGraceFully的基本逻辑如下:
每个eventLoop中的都有内部变量STATE_UPDATE用来维护该eventLoop的状态,当执行shutdownGraceFully方法时,会将状态设置为shutdown,在这个步骤中仅仅是设置状态即可
在eventLoop的轮询中,完成IO轮询和任务调度后,会执行以下操作
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
break;
}
}
其中if的判断就是会去检查状态,如果是shutdown,则会执行closeAll,注意是group执行的shutodown,所以group中管理的所有eventLoop均会轮询到closeAll方法中
closeAll会循环eventLoop上的多路复用器selector上绑定的所有channel,通过channel的unsafe依次关闭channel,释放线程池,完成关闭操作
如果是用户调用的channel.close则会封装为用户任务,在eventLoop的轮询中进行处理
对于任何架构,线程模型设计的好坏都直接影响软件的性能和并发处理能力