Netty源码阅读系列-服务端启动流程梳理

137 阅读3分钟

「我正在参与掘金会员专属活动-源码共读第一期,点击参与

一、流程梳理

Netty服务端的启动流程大概可以概括为几下几个步骤:

  1. channel创建。

  2. channel初始化与handler注册。

  3. 绑定端口。

  4. 监听accept事件。

1.channel创建

channel的创建是在较早的时刻完成的,AbstractBootstrap的initAndRegister在main线程中执行,利用反射工厂创建NioServerSocketChannel的实例。

NioServerScoketChannel对JDK ServerSocketChannel进行了包装,在底层创建了JDK的ServerSocketChannel实例,并将ServerSocketChannel的阻塞模式设置为非阻塞。

关于Netty Server channel创建更具体的分析可以参考Netty源码阅读系列-Channel创建

2.channel初始化与handler注册

同样分析AbstractBootstrap的initAndRegister方法。

在channel创建完成后调用了init(channel)方法,在该方法中对channel进行了一些配置,并向该channel的pipeline中添加了一个ChannelInitializer类型的handler,这是一个特殊的Handler,会在channel被注册到事件循环(EventLoop)之后触发initChannel回调,以便在合适的时机进一步装配channel。

从代码中可以看到,在initChannel回调中向channel的pipieline中添加了一个很关键的Handler,它就是ServerBootstrapAcceptor。

ServerBootstrapAcceptor负责对accept操作完成后产生的childChannel(代表与客户端之间的连接)进行配置,添加childHandler,将childChannel绑定到workGroup。

关于初始化流程更详细的分析可以参考Netty源码阅读系列-ServerBootstrap初始化流程

3.绑定端口

initAndRegister内的很多动作都并不是在main线程直接运行的,而是将task交给了bossGroup的线程池。

在代码内会使用向ChannelFuture加监听器的方式监听动作的完成以进行下一步操作。

看AbstractBootstrap类dobind方法的代码,在调用initAndRegister方法后会返回一个ChannelFuture类型的返回值regFuture,当channel的注册操作完成时会回调operationComplete方法,在该回调中开始端口的绑定操作。

image.png

image.png

image.png 可以看到bind的过程中的操作也都是交给bossGroup的线程池去异步执行的。

4.监听accept事件

dobind()执行完毕,也就是端口绑定操作执行完后,会触发channelActive事件。

image.png

触发了channelActive事件会调用DefaultChannelPipieline类中的channelActive()方法。

执行该方法中的readIfIsAutoRead()。

随着调用栈的深入会调用到AbstractNioChannel的doBeginRead()方法。

image.png

image.png

image.png 在AbstractNioChannel的doBeginRead()方法中,会通过调用selectionKey.interestOps(interestOps | readInterestOp)告诉选择器监听该channel的accept事件(值为16)。

二、总结

将Netty服务端启动中关键的操作绘制流程图如下:

image.png

在ServerBootstrap.bind方法被调用后,在main线程中进行了channel的创建和部分初始化。

channel在bossGroup中进行了register操作,并在register完成后添加Handler,进行端口bind。

在端口bind后触发channelActive事件,该事件的触发会调用AbstractNioChannel的doBeginRead方法,doBeginRead方法会控制selectionKey去监听accept事件(值为16)。

虽然Netty中关于channel的操作大多都是在bossGroup管理的线程池中异步执行的,但通过对异步事件结果的监听,仍对各操作的执行顺序进行了控制。