Netty服务端启动的四个过程
1.创建服务端Channel
2.初始化服务端Channel
3.注册到Selector
4.端口绑定
创建服务端Channel
创建服务端Channel的步骤:
-
通过bind方法追踪代码,如下:
可以看到下面的代码:
追踪进去可以看到:
看看该方法是怎样创建管道的
-
这个clazz是哪个类?
在利用辅助类进行配置信息的时候,我们传入了一个类,这个类就是我们配置信息传入的那个类
追踪一下:
这就是clazz的赋值情况 -
传入的NioSocketChannel是一个什么样的类?创建的细节是什么?
先来看四个类的继承关系以及在创建过程中的作用(顶层为父类,往下为子类)
AbstractChannel:(3).创建id,unsafe,pipline
|
AbstractNioChannel:(2).设置非阻塞:configureBlocking(false)
|
AbstractNioMessageChannel:
|
NioSocketChannel:(1).newSocket()方法调用JDK底层代码创建ServerSokcetChannel
创建tcp配置类NioServerSocketChannelConfig -
现在来看看为什么反射创建了NioSocketChannel创建了Channel了呢
先来看NioSocketChannel的构造函数做了什么
该构造函数调用了其他构造函数,传参是newSocket()方法的返回,看看newSocket()方法
这块代码就是调用JDK代码来创建Channel了,可以看到返回值是ServerSocketChannel,这个在NIO中比较熟悉了。
看一下被调用的构造函数
同时创建了TCP配置类NioServerSocketChannelConfig,这些就是括号(1)
下面跟随super进入上上一级父类AbstractNioChannel
可以看到NIO编程中的设置非阻塞模式,也就是括号(2)
继续跟踪父类AbstractChannel
这里创建了id,unsage,pipeline,也就是(3)
初始化服务端Channel
重点关注init()方法
- 四个步骤:
(1)set ChannelOptions,ChannelAttrs
(2)set ChildOptions,set ChildAttrs(还是在init方法中)
(3)配置pipeline
拿到pipeline后将用户添加的handler添加到该pipeline上
(4)在最后默认添加ServerBootstrapAcceptor(一个特殊的ChannelHandler)
- ServerBootstrapAcceptor是一个新连接接入器,构造的参数都是配置信息中所传入的信息
来看看这四个参数
跟踪出处
这是ServerBootstrap中的两个属性直接赋值过来的,而这两个属性就是在写代码时配置信息所配置的信息
分别点入用户代码的这两个方法
先点入group方法
可以看到childGroup是传入的参数
再跟踪childHandler
childHandler也是传入的
剩下的两个属性就是刚才初始化的两个属性
注册到selector上
同样是initAndRegister()方法
创建并且完成初始化后,就会调用register方法
register方法的结构图
所以注册到选择器需要重点关注register方法
这里留一个问题:
为什么追踪register方法追踪到的是EventLoopGroup,但是讲解这块展示的逻辑代码是AbstractChannel
-
看下register方法
追踪register0
追踪doRegister方法,查看真正注册的方法内容
下面看一下invokeHandlerAddedIfNeeded和fireChannelRegistered两个方法
这两个方法分别对应于Handler中的两个继承方法,如下:
-
最后看一下fireChannelActive方法,该方法不会被调用
因为netty在这里只是进行了selector绑定的操作,并没有进行端口的绑定的操作。所以isActive方法会返回false
服务端口的绑定
方法结构图
-
还是从doBind方法开始看,如下:
追踪doBind0方法
有一个问题:为什么追踪后是AbstractBootstrap的方法,但是讲解内容确是AbstarctChannel
下面看一下AbstractChannel的bind方法
doBind方法
bind方法调用了JDK底层的bind方法,javaChannel返回开始通过JDK底层创建的Channel
在这里真正完成了端口的绑定 -
下面看一段逻辑
这就调用了pipeline的fireChannelActive方法,也是一个传播事件,在初始化的时候当时没有调用这个fireChannelActive方法,所以在这里绑定完端口以后才调用。
关于后面为什么讲解又到了chnnelActive方法,而又调用了什么Autoread方法,这个不是很懂。