Netty服务端创建
先来看server bootstrap:是socket服务端的启动辅助类,用户通过它可以方便的创建netty的服务端
Netty服务端创建的步骤:
- 创建server bootstrap实例
- 设置并绑定reactor线程池。Netty 的 Reactor 线程池是 EventLoopGroup,它 实际就是 EventLoop 的数组。EventLoop 的职责是处理所有注册到本线程多路复用器 Selector 上的Channel,Selector 的轮询操作由绑定的 EventLoop 线程 run 方法驱动,在一 ,个循环体内循环执行。
- 设置并绑定服务端channel,作为NIO服务端,需要创建serversocketchannel,对应netty实现为Nioserver socket channel。Serverbootstrap也提供了channel方法用于指定服务端channel的类型,netty通过工厂类,利用反射创建Nioserver socket channel对象
- 创建channelpipeline,本质是一个负责处理网络事件的职责链,负责执行channel handler,
- 往Channelpipeline中添加channel handler
- 绑定并监听启动端口
- Selector轮询。由Reactor 线程NioEventLoop 负责调度和执行Selector 轮询操作,选择准备就绪的Channel集合
- 当轮询到就绪的channel之后,就由reactor线程NioEventloop执行channelpipeline的相应方法,最终执行channelhandler
- 执行channelhandler和用户添加的handler
Netty服务端创建源码分析
首先通过构造函数创建server bootstrap实例,创建两个Event loop group
NioEventLoopGroup 实际就是 Reactor 线程池,负责调度和执行客户端的接入、网络 读写事件的处理、用户自定义任务和定时任务的执行。通过ServerBootstrap的 group方法 将两个EventLoopGroup实例传入
创建Nioserversocketchannel,根据channel类型通过反射创建channel实例
服务端启动的最后一步就是绑定本地端口,启动服务
Nioserversocketchannel创建成功后,对他进行初始化
第一步设置socket参数和channel的附加属性
第二步将handler加入到channelpipeline中
之后最后一步就是注册Nioserversocketchannel到reactor线程的多路复用器上
先看看目前channelpipeline的组成
到这里服务端启动部分源码已经分析完成
客户端接入源码
当多路复用器检测到新的,准备就绪的Channel时,默认执行 processSelectedKeysOptimized 方法
由于监听的是客户端那边的连接操作,所以执行Unsafe.read()方法
分析domessage方法,实际上就是接受新的客户端连接并创建Niosocketchannel
接受新的客户端连接后,触发channelpipeline的channelread方法
Niosocketchannel的注册方法与server socket channel的一致,也是将channel注册到reactor线程的多路复用器上
执行完注册操作之后,紧接着会触发ChanncelReadComplete 事件。我们继续分析ChannelReadComplete 在ChannelPipeline 中的处理流程:Netty 的Header 和Tail 本身不关 注 ChannelReadComplete 事件就直接透传,执行完ChannelIReadComplete 后,接着执行 ,PipeLine 的 read()方法,最终执行HeadHandler的 read()方法 HeadHandlerread()方法的代码已经在之前的小节介绍过,用来将网络操作位修改为 操作。创建 NioSocketChannel 的时候已经将AbstractNioChanncl 的 readInterestOp 设置为OP_READ,这样,执行 selectionKey.interestOps(interestOpslreadInterestOp)操作时就会把 操作位设置为OP_READ
至此,新接入的客户端连接处理完成,可以进行读写
客户端创建
Bootstrap是客户端创建工具类
Netty客户端创建流程:
步骤1:用户线程创建Bootstrap实例,通过API设置创建客户端相关的参数,异步发 起客户端连接
步骤2:创建处理客户端连接、I/O读写的Reactor 线程组NioEventLoopGroup。可以 通过构造函数指定I/O线程的个数,默认为CPU 内核数的2倍;
步骤3:通过 Bootstrap的ChanneIFactory 和用户指定的Channel类型创建用于客户端 连接的NioSocketChannel,它的功能类似于JDKNIO类库提供的SocketChannel
步骤4:创建默认的Channel HandlerPipeline,用于调度和执行网络事件;
步骤5:异步发起TCP连接,判断连接是否成功。如果成功,则直接将NioSocketChannel 注册到多路复用器上,监听读操作位,用于数据报读取和消息发送;如果没有立即连接成 功,则注册连接监听位到多路复用器,等待连接结果;
步骤6:注册对应的网络监听状态位到多路复用器;
步骤7:由多路复用器在I/O现场中轮询各Channel,处理连接结果;
步骤8:如果连接成功,设置Future 结果,发送连接成功事件,触发 ChanneIPipcline 执行;
步骤9:由ChanneIPipeline 调度执行系统和用户的ChanneIHandler,执行业务逻辑
Boostrap提供了设置IO线程组的接口:
TCP参数设置接口:
Channel接口:用于指定客户端使用的channel接口(反射机制)
设置handler接口:
发起客户端连接:
客户端连接操作
首先要创建和初始化Niosocketchannel
从NioEventLoopGroup中 获 取 NioEventLoop,然后使用其作为参数创建 NioSocketChannel
初始化channel后,将其注册到selector上
发起异步TCP连接
Doconnect0最终调用head handler的connect方法
需要注意的是,SocketChannel 执行 connect()操作后有以下三种结果:(1)连接成功,返回True(2)暂时没有连接上,服务端没有返回ACK 应答,连接结果不确定,返回False; (3)连接失败,直接抛出I/O异常。 如 果 是 第 二 种 结 果,需 要 将NioSocketChannel 中 的 selectionKey 设 置 为 OP_ CONNECT,监听连接结果。 异步连接返回之后,需要判断连接结果,如果连接成功,则触发 ChannelActive事件
ChanelActive 事件最终会将 NioSocketChannel 中的 selectionKey 设置为SelectionKey.OP_READ,用于监听网络读操作。如果没有立即连接上服务端,则注册 SelectionKey.OP_CONNECT 到多路复用器
如果过程发生异常,则关闭链路
异步连接结果通知
当服务端返回握手应答之后,对连接结果进行判断
进一步看finishconnect方法:
Dofinishconnect方法用于判断JDK的socketChannel的连接结果
连接成功之后调用fulfillconnectPromiose方法,触发链路激活事件
Fire Channel Active方法主要修改网络监听位为读操作
客户端连接超时机制
首先,用户在创建Netty客户端的时候,可以通过ChannelOption.CONNECT TIMEOUT_MILIS配置项设置连接超时时间
发起连接的同时,启动连接超时检测定时器
如果在超时之前获取到结果,就删除定时器
无论是否连接成功,只要获取到结果,就删除连接超时定时器