一个简单的netty服务端
1 | public final class Server { |
- 服务端的socket在哪里初始化?
- 在哪里accept连接?
Netty服务端启动的过程
- 创建服务端Channel
调用JDK的API创建Channel,绑定一些基本组件 - 初始化服务端Channel
初始化一些基本属性,添加一些逻辑处理器 - 注册selector
将底层的Channel注册到事件轮询器selector上,再把服务端channel注册到底层 - 端口绑定
创建服务端Channel
1 | bind()[用户端代码入口] |
最终在channelFactory.newChannel()
中通过反射创建Channel。
1 | final ChannelFuture initAndRegister() { |
此时发现这个channelFactory
实际是一个叫ReflectiveChannelFactory
的类型,是他用反射创建的channel.
那么这个ReflectiveChannelFactory
是在哪里初始化的?
看一下上一节的代码中的
1 | .channel(NioServerSocketChannel.class)//设置服务端的channel |
中到底做了哪些事情。
点进去会发现是这么一段
1 | return channelFactory(new ReflectiveChannelFactory<C>(channelClass))//channelClass参数就是NioServerSocketChannel.class |
也就是这个时候帮他设置到里面去的,所以最终反射构 建的channel是NioServerSocketChannel.class
。
知道它通过反射把channel构造为NioServerSocketChannel后,去看看这个NioServerSocketChannel
的构造函数会做那些事。
反射构建服务端Channel
了解我们通过反射构造Channel的同时做了哪些事.
1 | newSocket()[通过jdk来创建底层jdk channel] |
newSocket()相关部分
以下基于 io.netty.channel.socket.nio.NioServerSocketChannel
源代码
NioServerSocketChannel的构造函数调用 newSocket()
,里面又调用provider.openServerSocketChannel();
创建并返回ServerSocketChannel
。ServerSocketChannel
是JDK底层的Channel。
这么一来底层的channel就创建好了.接下来构造函数会进入这么一段
1 | public NioServerSocketChannel(ServerSocketChannel channel) {//参数是刚才创建的底层channel |
NioServerSocketChannelConfig()相关部分
NioServerSocketChannel
的构造函数中调用了这么一个:
1 | config = new NioServerSocketChannelConfig(this, javaChannel().socket()); |
方便后期对TCP进行配置.
AbstractNioChannel()相关部分
再看它的父类构造器,追踪到AstractNioChannel
的构造函数里面会看到
1 | ch.configureBlocking(false); //这里的ch是刚才创建的jdk底层的channel,把它设为非阻塞 |
这么一段.把channel设置为非阻塞流
继续追踪AbstractNioChannel
的父类,到了AbstractChannel
之后,会看到它的构造函数为
1 | protected AbstractChannel(Channel parent) { |
初始化服务端Channel
再看创建服务端Channel时的过程,他之后将会调用init()
方法.
1 | bind()[用户端代码入口] |
那么这个初始化会做哪些事
1 | init()[初始化服务端Channel] |
set ChannelOptions,ChannelAttrs
目的是设置用户自定义的服务端配置.
在io.netty.bootstrap.ServerBootstrap#init
中:
1 | void init(Channel channel) throws Exception { |
这里配置的是通过以下的方式配置的,第一节中的代码示例中没有配.
1 | ServerBootstrap b = new ServerBootstrap(); |
set childOption,childAttr
这一步是为了设置用户自定义的连接配置,为每个新连接都配置. 值是用户代码中.childOption()
和.childAttr()
配置的部分.
同样在io.netty.bootstrap.ServerBootstrap#init
中:
1 | synchronized (childOptions) { |
config handler
配置完成后接下来在,在pipeline中把用户自定义的handler放入.这个handler是用户代码中通过.handler()设置的部分.同样是io.netty.bootstrap.ServerBootstrap#init
.
1 | ChannelPipeline p = channel.pipeline(); |
config.handler()是如何获取用户设置的handler的?
首先得知道,客户端代码进行.handler(/**/)
的时候,调用的是AbstractBootstrap#handler
,此时只会单纯把这个handler存到AbstractBootstrap
的一个handler属性中.config.handler()
就是返回这个handler属性的值.后面的.childHandler()
等配置取值都是通过类似方式.
add ServerBootstrapAcceptor
这一步的目的是给accept添加一个NIO的线程,还是接着看io.netty.bootstrap.ServerBootstrap#init
.上一步操作后,他会
1 | p.addLast(new ChannelInitializer<Channel>() { |
注册selector
回到io.netty.bootstrap.AbstractBootstrap#initAndRegister
方法.构造channel,channel初始化的两步完成后会调用register方法
1 | Channel channel = null; |
步骤:
1 | io.netty.channel.AbstractChannel.AbstractUnsafe#register |
this.eventLoop = eventLoop
实际代码
1 | AbstractChannel.this.eventLoop = eventLoop; |
之后的所有操作都由这个eventLoop处理,之后调用register0()
register0()
io.netty.channel.AbstractChannel.AbstractUnsafe#register0
,做了三件事:
1 | doRegister(); //把jdk channel注册到selector |
doRegister()
位置:io.netty.channel.nio.AbstractNioChannel#doRegister
里边调用JDK底层的一个注册方法,把jdk channel注册到selector
1 | selectionKey = javaChannel()//返回之前创建服务端channel时创建的底层Channel |
实际上,注册一个channel的时候是调用jdk底层的一个api去注册到selector中
并把这个this,就是服务端的channel,也一同附加进去,
之后selector轮询到jdk channel时通过获取attachment就能取出netty服务端的Channel,从而进行特定的传播.
invokeHandlerAddedIfNeeded和fireChannelRegistered
由于在用户代码中通过.handler(new ServerHandler())
进行了绑定.
invokeHandlerAddedIfNeeded和fireChannelRegistered分别调用用户代码中的com.imooc.netty.ch3.ServerHandler#channelRegistered
和com.imooc.netty.ch3.ServerHandler#handlerAdded
但注意,register0()
中下面还有个pipeline.fireChannelActive();`:
1 | if (isActive()) { //此时返回false |
但此时由于isActive()
返回false,这个fireChannelActive
是不会被触发的.
端口绑定
1 | AbstractUnsafe.bind()[入口] |
回到io.netty.bootstrap.AbstractBootstrap#doBind
,然后->bind0
->..->io.netty.channel.AbstractChannel.AbstractUnsafe#bind
.在里面会看到实际绑定本地端口的doBind()
和active事件传播的pipline.fireChannelActive()
.
doBind()
这里实际调用的是io.netty.channel.socket.nio.NioServerSocketChannel#doBind
. 通过
1 | javaChannel()//之前创建的jdk底层channel |
对一个端口进行绑定.
pipline.fireChannelActive()
代码相关部分(AbstractUnsafe#bind
):
1 | boolean wasActive = isActive();//还没绑定,false |
fireChannelActive,实际上不只是单纯的触发active事件.追踪pipeline.fireChannelActive()
->DefaultChannelPipeline#fireChannelActive
->AbstractChannelHandlerContext#invokeChannelActive
->io.netty.channel.DefaultChannelPipeline.HeadContext#channelActive
这个HeadContext#channelActive
的代码如下:
1 |
|
怎样实现重新绑定accept事件?
跟踪这个readIfIsAutoRead()
->..->io.netty.channel.AbstractChannelHandlerContext#read
->..io.netty.channel.AbstractChannel.AbstractUnsafe#beginRead
->..->io.netty.channel.nio.AbstractNioChannel#doBeginRead
doBeginRead的内容如下
1 |
|
回顾一下readInterestOp
是从哪来的.在NioServerSocketChannel
的构造函数中就带入了
1 | super(null, channel, SelectionKey.OP_ACCEPT); //加入accept事件 |
readIfIsAutoRead()
的逻辑完成后,当收到OP_ACCEPT事件的时候就会交给netty处理.