深入解析Netty服务端创建

7.服务端创建(重点)

Netty为了向使用者屏蔽NIO通信的底层细节,在和用户交互的边界做了封装,目的就是为了减少用户开发工作量,降低开发难度。ServerBootStrap是Socket服务端的启动辅助类。

1.Netty服务端创建时序图
深入解析Netty服务端创建_第1张图片

(1)创建ServerBootstrap实例,它是Netty服务端的启动辅助类,提供了一系列的方法用于设置服务端启动相关的参数。底层通过门面模式对各种能力进行抽象和封装,尽量不需要用户跟过多的底层API打交道。

它的实例只有一个无参的构造函数,作为启动辅助类这让人不可思议,因为它需要与多个组件或者类交互。ServerBootstrap构造函数没有参数的根本原因是因为它的参数太多了,而且未来也可能会发生变化,为了解决这个问题,就需要引入Builder模式。

(2)设置并绑定Reactor线程池。Netty的Reactor线程池是EventLoopGroup,它实际就是EventLoop的数组。EventLoop的职责是处理所有注册到本线程多路复用器Selector上Channel,Selector的轮询操作由绑定的EventLoop线程run方法驱动,在一个循环体内循环执行。

PS:EventLoop的职责不仅仅是处理网络IO事件,用户自定义的Task和定时任务Task也统一由EventLoop负责处理,这样线程模型就实现了统一。从调度层面看,也不存在从EventLoop线程中再启动其他类型的线程用于异步执行另外的任务,这样就避免了多线程并发操作和锁竞争,提升了IO线程的处理和调度性能。

(3)设置并绑定服务端Channel。作为NIO服务端,需要创建ServerSocketChannel。Netty对原生的NIO类库进行了封装,对应实现是NioServerSocketChannel。

Netty的ServerBootstrap方法提供了channel方法用于指定服务端的Channel类型。

Netty通过工厂类,利用反射创建NioServerSocketChannel。由于服务端监听端口往往只需要系统启动时才会调用,因此反射对性能的影响并不大。

(4)链路建立的时候创建并初始化ChannelPipeline。ChannelPipeline并不是NIO服务端必需的。它本质就是一个负责处理网络事件的职责链,负责管理和执行ChannelHandler。网络事件以事件流的形式在ChannelPipeline中流转,由ChannelPipeline根据ChannelHandler的执行策略调度ChannelHandler的执行。

典型的网络事件如下:

(1)链路注册  (2)链路激活   (3)链路断开   (4)接收到请求消息   (5)请求消息接收并接受完毕

(6)发送应答消息   (7)链路发生异常   (8)发生用户自定义事件

(5)初始化ChannelPipeline完成之后,添加并设置ChannelHandler。ChannelHandler是Netty提供给用户定制和扩展的关键接口。Netty也提供了大量的系统ChannelHandler供用户使用,如下:

(1)系统编解码框架——ByteToMessageCodec

(2)通过基于长度的半包解码器——LengthFieldBasedFrameDecoder

(3)码流日志打印Handler——LoggingHandler

(4)SSL安全认证Handler——SslHandler

(5)链路空闲检测Handler——IdleStateHandler

(6)流量整形Handler——ChannelTrafficShapingHandler

(7)Base64编解码——Base64Decoder和Base64Encoder

(6)绑定并启动监听端口。在绑定监听端口之前系统会做一系列的初始化和检测工作,完成之后,会启动监听端口,并将ServerSocketChannel注册到Selector上监听客户端连接。

(7)Selector轮询。由Reactor线程NioEventLoop负责调度和执行Selector轮询操作,选择准备就绪的Channel集合。

(8)当轮询到准备就绪的Channel之后,就由Reactor线程NioEventLoop执行ChannelPipeline的相应方法,最终调度并执行ChannelHandler。

(9)执行Netty系统ChannelHandler和用户添加定制的ChannelHandler。ChannelPipeline根据网络事件的类型,调度并执行ChannelHandler。

2.Netty服务端创建源码分析

  1. 首先通过构造函数创建ServerBootstrap实例,随后,通常会创建2个EventLoopGroup。一个用于连接客户端,一个用于处理IO。

  2. NioEventLoopGroup实际就是Reactor线程池,负责调度和执行客户端的接入、网络读写事件的处理、用户自定义和定时任务的执行。通过ServerBootstrap的group方法将2个EventLoopGroup实例传入。

  3. 线程组和线程类型设置完成后,需要设置服务端Channel用于端口监听和客户端链路接入。Netty通过Channel工厂类来创建不同类型的Channel,对于服务端,需要创建NioServerSocketChannel。所以,通过指定Channel类型的方式创建Channel工厂。

  4. ServerBootstrapChannelFactory是ServerBootstrap的内部静态类,职责是根据Channel的类型通过反射创建Channel的实例。

  5. 指定NioServerSocketChannel后,需要设置TCP的一些参数,作为服务端,主要是要设置TCP的backlog参数。
    backlog指定了内核为此套接字排队的最大连接个数,对于给定的监听套接口,内核要维护2个队列:未连接队列和已连接队列。服务器处于listen状态时,收到客户端connect时在未完成队列中创建一个新的条目,然后用服务器的syn响应客户端,在客户端对服务器syn的ack之前一直保留在未完成连接队列中。如果三路握手完成,该条目将从未完成连接队列搬到已完成连接队列尾部。
    当进行调用accept时,从已完成队列中的头部取出一个条目给进程,当已完成队列为空时进程将睡眠,直到有条目在已完成连接队列中才唤醒。backlog被规定为2个队列总和的最大值。Netty默认的backlog为100.

  6. TCP参数设置完成后,用户可以为启动辅助类和其父类分别指定Handler。2类Handler的用途不同:子类中的Handler是NioServerSocketChannel对于的ChannelPipeline的Handler;父类中的Handler是客户端新接入的连接SocketChannel对应的ChannelPipeline的Handler。

  7. 本质区别就是:ServerBootstrap中的Handler是NioServerSocketChannel使用的,所有连接该监听端口的客户端都会执行它;父类AbstractBootstrap中的Handler是个工厂类,它为每个新接入的客户端都创建一个Handler。

  8. 服务端启动的最后一步,就是绑定本地端口,启动服务。

  9. NioServerSocketChannel创建成功后,对它进行初始化,主要有以下3点:
    (1)设置Socket参数和NioServerSocketChannel的附加属性。
    (2)将AbstractBootstrap的Handler添加到NioServerSocketChannel的ChannelPipeline中
    (3)将用于服务端注册的Handler添加到ChannelPipeline中。

  10. 到此,Netty服务端监听的相关资源以及初始化完毕,就剩下最后一步——注册NioServerSocketChannel到Reactor线程的多路复用器上,然后轮询客户端连接事件。
    (1)首先判断是否是NioEventLoop自身发起的操作。如果是,则不存在并发操作,直接执行Channel注册;如果由其他线程发起,则封装成一个Task放入消息队列中异步执行。
    (2)注册成功后,触发ChannelRegistered事件。
    (3)ChannelRegistered事件传递完成后,判断ServerSocketChannel是否监听成功,如果成功,需要触发NioServerSocketChannel的ChannelActive事件。
    (4)ChannelActive事件在ChannelPipeline中传递,完成之后根据配置决定是否自动触发Channel的读操作。
    (5)AbstractChannel的读操作触发ChannelPipeline的读操作,最终调用到HeadHandler的读方法。

  11. 在某些场景下,当前监听的操作类型和Channel关心的网络事件是一致的,不需要重复注册,所以增加了&操作的判断,只有两者不一致,才需要重新注册操作位。

    由于只有4种网络操作类型,所以用4bit就可以表示所有的网络操作位。这样能非常方便通过位操作来进行网络操作位的状态判断和状态修改,从而提升操作性能。

3.客户端接入源码分析

  1. 负责处理网络读写、连接和客户端请求接入的Reactor线程都是NioEventLoop。
  2. 当多路复用器检测到新的准备就绪的Channel时,默认执行processSelectedKeysOptimized。
  3. 由于Channel的Attachment是NioServerSocketChannel,所以执行processSelectedKey方法,根据就绪的操作位,执行不同的操作。
  4. 接收到新的客户端连接后,触发ChannelPipelined的ChannelRead方法。

你可能感兴趣的:(深入解析Netty,java,服务器)