Netty源码分析-服务器端启动流程
参考文献
- 黑马Netty
环境
- 基于
Netty4.1.92.Final-SNAPSHOT
启动流程
-
Netty
中启动流程大致处理步骤1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//1 netty 中使用 NioEventLoopGroup (简称 nio boss 线程)来封装线程和 selector
Selector selector = Selector.open();
//2 创建 NioServerSocketChannel,同时会初始化它关联的 handler,以及为原生 ssc 存储 config
NioServerSocketChannel attachment = new NioServerSocketChannel();
//3 创建 NioServerSocketChannel 时,创建了 java 原生的 ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
//4 启动 nio boss 线程执行接下来的操作
//5 注册(仅关联 selector 和 NioServerSocketChannel),未关注事件
SelectionKey selectionKey = serverSocketChannel.register(selector, 0, attachment);
//6 head -> 初始化器 -> ServerBootstrapAcceptor -> tail,初始化器是一次性的,只为添加 acceptor
//7 绑定端口
serverSocketChannel.bind(new InetSocketAddress(8080));
//8 触发 channel active 事件,在 head 中关注 op_accept 事件
selectionKey.interestOps(SelectionKey.OP_ACCEPT); -
创建服务器端示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22final NioEventLoopGroup boosGroup = new NioEventLoopGroup(MAIN_THREAD_NUMBER);
final NioEventLoopGroup workerGroup = new NioEventLoopGroup();
final ServerBootstrap serverBootstrap = new ServerBootstrap();
try {
serverBootstrap.group(boosGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler("DEBUG"))
.childHandler(initializer);
final ChannelFuture channelFuture = serverBootstrap.bind(port);
channelFuture.addListener(listener -> {
if (listener.isSuccess()) {
LOGGER.info("WebSocket绑定端口:{}成功", port);
} else {
LOGGER.warn("WebSocket绑定端口:{}失败", port);
}
});
final Channel channel = channelFuture.sync().channel();
channel.closeFuture().sync();
} finally {
boosGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
入口
io.netty.bootstrap.AbstractBootstrap#bind(java.net.SocketAddress)
- 所处线程:
main
1 | /** |
1 | private ChannelFuture doBind(final SocketAddress localAddress) { |
1 | final ChannelFuture initAndRegister() { |
创建NioServerSocketChannel
1 | final ChannelFuture initAndRegister() { |
- 执行后会进入
NioServerSocketChannel
的默认构造方法
1 | /** |
- 使用默认构造函数会依次调用上面三个带参数的构造函数
newChannel(provider, family)
最终使用provider.openServerSocketChannel()
来创建ServerSocketChannel
1 |
|
此处的ChannelFactory
为什么会是io.netty.channel.ReflectiveChannelFactory
-
因为在示例中,我们通过设置
.channel(NioServerSocketChannel.class)
指定了ChannelFactory
为io.netty.channel.ReflectiveChannelFactory
1
2
3
4serverBootstrap.group(boosGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler("DEBUG"))
.childHandler(initializer);1
2
3
4
5public B channel(Class<? extends C> channelClass) {
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
初始化Channel
- 以创建
NioServerSocketChannel
为例 io.netty.bootstrap.ServerBootstrap#init
- 所处线程:
main
1 |
|
pipeline
中的handler
为head-->ServerBootstrapAcceptor-->tail
注册
- 调用链
1 | io.netty.channel.EventLoopGroup#register(io.netty.channel.Channel) |
1 |
|
- 此时
register0
所属线程为NIO
线程
1 | private void register0(ChannelPromise promise) { |
1 |
|
绑定端口
- 入口:
io.netty.bootstrap.AbstractBootstrap#doBind0
1 | private static void doBind0( |
bind
的调用链
1 | io.netty.channel.ChannelOutboundInvoker#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise) |
1 |
|
执行端口绑定
1 |
|
触发 active 事件
-
调用链
1
2
3
4
5
6
7
8
9
10
11
12io.netty.channel.DefaultChannelPipeline#fireChannelActive
-->io.netty.channel.AbstractChannelHandlerContext#invokeChannelActive(io.netty.channel.AbstractChannelHandlerContext)
-->io.netty.channel.AbstractChannelHandlerContext#invokeChannelActive()
-->io.netty.channel.DefaultChannelPipeline.HeadContext#channelActive
-->io.netty.channel.DefaultChannelPipeline.HeadContext#readIfIsAutoRead
-->io.netty.channel.AbstractChannel#read
-->io.netty.channel.DefaultChannelPipeline#read
-->io.netty.channel.AbstractChannelHandlerContext#read
-->io.netty.channel.AbstractChannelHandlerContext#invokeRead
-->io.netty.channel.DefaultChannelPipeline.HeadContext#read
-->io.netty.channel.AbstractChannel.AbstractUnsafe#beginRead
-->io.netty.channel.nio.AbstractNioChannel#doBeginRead
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 HoleLin's Blog!