一、前言
在理论开发过程中,通过Netty提供的高度封装的Api,咱们能够很容易地构建出本人的服务端程序,如下例
public static void main(String[] args) throws Exception {
// 实例化bossGroup和workerGroup
// bossGroup传入参数1,示意只蕴含一个EventLoop
// workerGroup应用无参构造函数,默认实例化 (CPU核数 * 2)个EventLoop
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // 指定Channel的类型,该例子是服务端,所以应用NioServerSocketChannel
.option(ChannelOption.SO_BACKLOG, 100) // 设置NioServerSocketChannel的option选项
.handler(new LoggingHandler(LogLevel.INFO)) // 设置NioServerSocketChannel对应的ChannelPipeline流水线上的ChannelHandler
.childHandler(new ChannelInitializer<SocketChannel>() { // 设置NioSocketChannel对应的ChannelPipeline流水线上的ChannelHandler
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new EchoServerHandler()); // 增加EchoServerHandler处理器
}
});
// 启动服务器
ChannelFuture f = b.bind(PORT).sync();
// 阻塞直到NioServerSocketChannel敞开
f.channel().closeFuture().sync();
} finally {
// 敞开所有EventLoop来终止所有线程
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
- 这里的bossGroup只蕴含一个NioEventLoop,该NioEventLoop次要用于解决ACCEPT事件
- workerGroup默认实例化 (
CPU核数 * 2
) 个NioEventLoop
下面这是个源码中的简略例子EchoServer,作用是接管客户端发送过去的音讯,并写回客户端
。借这个例子,来对服务端的启动流程进行解读
二、服务端的启动流程
服务端的启动次要与两个线程无关,别离是main线程
和bossGroup的NioEventLoop对应的线程
,这外面假设该线程名为NioEventLoopGroup2-1。上面来看下这两个线程的执行流程。
2.1 main线程的执行流程
首先咱们看到main办法中是通过ServerBootstrap的bind()办法
来启动服务器的,所以这里就是入口。上面是main线程的执行流程图
能够看到,main办法次要做了两件事件:
- initAndRegister:
初始化NioServerSocketChannel,并注册到bossGroup的NioEventLoop上
(因为这里bossGroup只有一个EventLoop,所以就是固定的那一个),并返回ChannelFuture(因为是异步注册) - addListener:给异步注册返回的ChannelFuture对象
增加监听器
,在注册胜利之后,会告诉该监听器,该监听器外部会执行doBind0办法来提交【绑定端口】工作给该NioEventLoop
当初来看一下它们的具体步骤
1)initAndRegister
能够看到,其外部次要有3个步骤:
- 通过ReflectiveChannelFactory类的newChannel()办法来
实例化NioServerSocketChannel
(对ServerSocketChannel的封装) - 调用init办法来初始化NioServerSocketChannel,设置它的options和attributes,并且调用ChannelPipeline类的addLast办法,将
ChannelInitializer增加到它的ChannelPipeline流水线
中 - 提交工作【NioServerSocketChannel注册】给bossGroup的NioEventLoop上
2)addListener
上一步initAndRegister执行之后会返回一个名为regFuture的ChannelFuture对象,如果该regFuture已实现,间接执行doBind0办法,否则增加监听器,当regFuture实现时失去告诉,执行doBind0(该办法外部是提交【绑定端口】工作给该NioEventLoop
),如下
到这里,main线程的启动流程就完结了,接下来来看下bossGroup的NioEventLoopGroup2-1线程做了哪些事件?
2.2 【bossGroup】NioEventLoopGroup2-1线程的执行流程
在下面main线程的执行过程中,提交了一个【NioServerSocketChannel注册】工作给bossGroup的NioEventLoop,此时该NioEventLoop接管到了第一个工作,开始进行初始化工作,启动线程并绑定,开始执行NioEventLoop类的run办法
,run办法的步骤如下:
在之前的文章中说过,NioEventLoop自身是死循环来一直解决接管到的工作
,所以这里也是一样,开始执行NioEventLoop的run办法,其外部执行了runAllTasks办法来执行所有工作(留神:在执行工作的过程中也能够提交工作给该NioEventLoop
)
接下来就来看下runAllTasks办法的执行流程图
能够看到,它的次要内容是执行了4个工作,别离是
- 执行工作【NioServerSocketChannel注册】
- 执行工作【ChannelPipeline增加ServerBootstrapAcceptor】
- 执行工作【绑定端口】
- 执行工作【激活NioServerSocketChannel】
那大家可能会有疑难,为什么main线程里明明只提交了一个工作,这里却要执行4个工作?答案就是在工作执行的过程中,又提交了新的工作给该NioEventLoop
。先来看下执行第一个工作
1)执行工作【NioServerSocketChannel注册】
执行工作【NioServerSocketChannel注册】,次要有4个步骤
- 调用AbstractNioChannel的doRegister办法,将该Channel外部
java nio的ServerSocketChannel注册到该NioEventLoop的Selector
- 之前main线程的执行过程中将ChannelInitializer增加到ChannelPipeline中,这里调用该ChannelInitializer的handlerAdded办法,
将配置的LoggingHandler增加到ChannelPipeline中,并在ChannelPipeline中移除本身
,并提交工作【ChannelPipeline增加ServerBootstrapAcceptor入站处理器】
所以以后的ChannelPipeline里的程序是这样的:HeadContext -> LoggingHandler -> TailContext
- 设置工作执行胜利,因为main线程中对regFuture增加了监听器,所以这里会告诉到监听器,监听器外部提交工作【绑定端口】
- 最初一步,调用以后ChannelPipeline的fireChannelRegistered办法,从ChannelPipeline的head开始遍历,顺次往后找重写了channelRegistered办法的ChannelHandler,如果在channelRegistered办法里调用了ctx.fireChannelRegistered(),则持续往后遍历
到这里,第一个工作【NioServerSocketChannel注册】就执行实现了。
2)执行工作【ChannelPipeline增加ServerBootstrapAcceptor】
第二个工作是往ChannelPipeline增加ServerBootstrapAcceptor处理器
,该处理器作用是接管新连贯并注册到workerGroup的NioEventLoop上
,前面会应用到。
3)执行工作【绑定端口】
第三个工作是绑定端口,会先从ChannelPipeline的tail开始遍历,顺次往前查找重写了bind办法的ChannelHandler,如果在bind办法里调用ctx.bind(),则持续往前遍历。在最初调用了HeadContext的bind办法,执行了ServerSocketChannel.bind()来绑定端口
,并且提交工作【激活NioServerSocketChannel】。
4)执行工作【激活NioServerSocketChannel】
激活NioServerSocketChannel的最终目标就是设置SelectionKey的监听事件,NioServerSocketChannel是ACCEPT事件
。具体步骤是
- 最先调用ChannelPipeline的fireChannelActive办法,从ChannelPipeline的head开始遍历,顺次往后调用ChannelHandler的channelActive办法
- 而在HeadContext的channelActive办法中,执行了readIfIsAutoRead办法,又调用了ChannelPipeline的read办法,从tail开始遍历,顺次往前调用ChannelHandler的read办法
- 在HeadContext的read办法里,底层通过SelectionKey.interestOps来设置NioServerSocketChannel的监听事件
runAllTasks办法执行完之后,NioEventLoop的run办法回到循环体头部,计算策略之后通过调用Selector的select()办法来阻塞以后线程
(当有工作被增加时,会通过Selector的wakeUp办法来被动唤醒该Selector)
三、总结
至此,服务端的启动流程就告一段落,后续会持续解说该bossGroup的NioEventLoop会如何解决接管到的连贯
,以及这些新的连贯是如何注册到workerGroup的NioEventLoop上
的。
发表回复