以下分析只讲NIO
使用java nio做网络编程大致流程如下
这个流程有哪些可以优化的空间?
Netty是对java网络框架的包装,它本身肯定也会有类似的处理流程。必定在这个方面做了自己的优化处理
获得Selector
使用Netty的时候都会用到对应的EventLoopGroup,它实际上就完成了Selector的初始化过程
Netty自定义了SelectionKey的集合,做了层包装,实际将Selector只有1个SelectorKey的集合换成了默认的两个集合
获得Channel
使用Netty时会执行channel的类型,然后在执行bind方法时,此处就会对channel实行初始化
构建的方式为
class.newInstance()
,以NioServerSocketChannel为例,它执行的就是对应的无参构造函数。
public NioServerSocketChannel() {
//newSocket即返回java的ServerSocketChannel
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(ServerSocketChannel channel) {
//指定当前channel用来接收连接请求,并在父类中指定为非阻塞
super(null, channel, SelectionKey.OP_ACCEPT);
//javaChannel()即这里的参数channel
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
复制代码
紧接着Netty开始channel的初始化,在NioServerSocketChannel的pipeline最后添加了一个
ChannelInboundHandlerAdapter
即
ServerBootstrapAcceptor
,它会执有
childGroup
和
childHandler
,childHandler即用户自定义的channelHandler,而childGroup则是处理请求所用的EventLoop,此时整个pipeline的结构为
childGroup为源码中字段的命名,对应为group中传递的worker线程池
channel的注册与监听端口地址关联
注册即建立channel和Selector的关系,值得注意的是,注册使用的线程池为
group
,对应用户传入的线程池即boss线程池,注册和端口、地址关联则顺着Netty的启动流程进行
至此可以看到,java nio所需要的准备工作都已经准备好了,剩下的就是等待事件发生以及处理发生的事件。与普通java nio的不同之处在于
- Netty准备了两个线程池,channel注册、端口绑定监听的只用到了其中同一个线程池
等待事件发生
NioEventLoop实现了Executor,意味着它接受其它地方提交任务给它执行,execute的大致结构如下
//判断当前正在执行的线程是否是Netty自己的eventLoop中保存的线程
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
//往队列里添加任务
addTask(task);
} else {
//这里即运行NioEventLoop自身的run方法
startThread();
addTask(task);
}
复制代码
NioEventLoop启动线程执行run方法,整体结构如下
for (;;) {
if (hasTasks()) {
selectNow();
} else {
select(oldWakenUp);
}
processSelectedKeys();
runAllTasks();
}
复制代码
run循环处理的流程如下