Netty客户端创建

1、Netty客户端创建时序图

Netty客户端创建

 

2、Netty客户端创建流程分析

步骤1:用户线程创建Bootstrap实例,通过API设置创建客户端相关的参数,异步发起客户端连接;

步骤2:创建处理客户端连接、I/O读写的Reactor线程组NioEventLoopGroup。可以通过构造函数指定I/O线程的个数,默认为CPU内核数的2倍;

步骤3:通过Bootstrap的ChannelFactory和用户指定的Channel类型创建用于客户端连接的NioSocketChannel,它的功能类似于JDK NIO类库提供的SocketChannel;

步骤4:创建默认的ChannelHandlerPipeline,用于调度和执行网络事件;

步骤5:异步发起TCP连接,判断连接是否成功。如果成功,则直接将NioSocketChannel注册到多路复用器上,监听读操作位,用于数据报读取和消息发送;如果没有立即连接成功,则注册连接监听位到多路复用器,等待连接结果;

步骤6:注册对应的网络监听状态位到多路复用器;

步骤7:由多路复用器在I/O现场中轮询各Channel,处理连接结果;

步骤8:如果连接成功,设置Future结果,发送连接成功事件,触发ChannelPipeline执行;

步骤9:由ChannelPipeline调度执行系统和用户的ChannelHandler,执行业务逻辑。

 

3、客户端连接辅助类Bootstrap

①设置I/O线程组:非阻塞I/O的特点就是一个多路复用器可以同时处理成百上千条链路,这就意味着使用NIO模式一个线程可以处理多个TCP连接。考虑到I/O线程的处理性能,大多数NIO框架都采用线程池的方式处理I/O读写,Netty也不例外。客户端相对于服务端,只需要一个处理I/O读写的线程组即可。

Netty客户端创建

②TCP参数设置接口:无论是异步NIO,还是同步BIO,创建客户端套接字的时候通常都会设置连接参数,例如接收和发送缓冲区大小、连接超时时间等。

Netty客户端创建

Netty提供的主要TCP参数如下

1)SO_TIMEOUT:控制读取操作将阻塞多少毫秒。如果返回值为0,计时器就被禁止了,该线程将无限期阻塞;

2)SO_SNDBUF:套接字使用的发送缓冲区大小;

3)SO_RCVBUF:套接字使用的接收缓冲区大小;

4)SO_REUSEADDR:用于决定如果网络上仍然有数据向旧的ServerSocket传输数据,是否允许新的ServerSocket绑定到与旧的ServerSocket同样的端口上。SO_REUSEADDR选项的默认值与操作系统有关,在某些操作系统中,允许重用端口,而在某些操作系统中不允许重用端口;

5)CONNECT_TIMEOUT_MILLIS:客户端连接超时时间,由于NIO原生的客户端并不提供设置连接超时的接口,因此,Netty采用的是自定义连接超时定时器负责检测和超时控制;

6)TCP_NODELAY:**或禁止TCP_NODELAY调节自选项,它决定是否使用Nagle算法。如果是时延敏感的应用,建议关闭Nagle算法。

 

③channel接口:用于指定客户端使用的channel接口,对于TCP客户端连接,默认使用NioSocketChannel。

Netty客户端创建

Netty客户端创建

 

④设置Handler接口:Bootstrap为了简化Handler的编排,提供了ChannelInitializer,它继承了ChannelHandlerAdapter,当TCP链路注册成功之后,调用initChannel接口,用于设置用户ChannelHandler。

Netty客户端创建

Netty客户端创建

Netty客户端创建

 

 

 

4、客户端连接操作

创建和初始化NioSocketChannel

Netty客户端创建

Netty客户端创建

Netty客户端创建

 

 

从NioEventLoopGroup中获取NioEventLoop,然后使用其作为参数创建NioSocketChannel,

Netty客户端创建

初始化换Channel之后,将其注册到Selector上,

Netty客户端创建

链路创建成功之后,发起异步的TCP连接,

Netty客户端创建

Netty客户端创建

 

需要注意的是,SocketChannel执行connect()操作之后有以下三种结果。

a、连接成功,返回true;

b、暂时没有连接上,服务的没有返回ACK应答,连接结果不确定,返回false;

c、连接失败,直接抛出I/O异常。

如果是第二种结果,需要将NioSocketChannel中的selectionKey设置为OP_CONNECT,监听连接结果。

异步连接返回之后,需要判断连接结果,如果连接成功,则出发ChannelActive事件,

Netty客户端创建

Netty客户端创建

 

5、异步连接结果通知

NioEventLoop的Selector轮询客户端连接Channel,当服务端返回握手应答之后,对连接结果进行判断。

Netty客户端创建

下面对finishConnect()进行分析

Netty客户端创建

doFinishConnect用于判断JDK的SocketChannel的连接结果,如果返回true表示连接成功,其他值或者发生异常表示连接失败。

Netty客户端创建

连接成功之后,调用fulfillConnectPromise方法,触发链路**事件,该事件由ChannelPipeline进行传播,

Netty客户端创建

Netty客户端创建

 

Netty客户端创建

 

 

6、客户端连接超时机制

对于SocketChannel接口,JDK并没有提供连接超时机制,需要NIO框架或者用户自己扩展实现。Netty利用定时器提供了客户端超时控制功能。

首先,用户在创建Netty客户端的时候,可以通过ChannelOption.CONNECT_TIMEOUT_MILLIS配置项设置连接超时时间

Netty客户端创建

AbstractNioChannel中发起连接的同时,启动连接超时检测定时器

Netty客户端创建

一旦超时定时器执行,说明客户端连接超时,构造连接超时异常,将异常结果设置到connectPromise中,同时关闭客户端连接,释放句柄。

如果在连接超时之前获取到连接结果,则删除连接超时定时器,防止其被触发。

Netty客户端创建