前言
- 分析之前,提出已下问题:
1.1 Netty是在哪里检测有新连接接入的?
1.2 新连接是怎样注册到NioEventLoop线程的? - 本篇文章按照下图顺序进行分析
检测新连接
- 通过上述截图以及之前文章可知,netty通过selector#select获取IO就绪的SelectionKey
1.1 获取SelectionKey的事件:int readyOps = k.readyOps();
1.2 如果readyOps为SelectionKey.OP_ACCEPT,则检测到新连接 - 获取SelectionKey附属的NioServerSocketChannel
2.1 调用NioServerSocketChannel的unsafe进行读取(见下图)
2.2 将jdk的底层channel封装在NioSocketchannel进行初始化(具体创建详情见下面分析)
创建NiosocketChannel
- 通过上图的总结可知,NiosocketChannel初始化主要配置为非阻塞以及保存感兴趣的读事件,禁止Nagle算法
1.1 初始化内容和服务端的NioServerSocketChannel部分相似,下面对此进行分析一下 - 下图列出Channel的继承关系
2.1 首先关注第一个抽象类AbstractChannel,主要做了id、unsafe以及pipeline的创建
2.1.1 unsafe:每一种Channel的读写抽象
(1) NioByteUnsafe:读取新连接
(2) NioMessageUnsafe:读取IO数据
2.2 AbstractNioChannel主要保存jdk底层channel、保存Channel关心的事件
(1)服务端Channel注册的事件为OP_ACCEPT,客户端Channel注册的事件为OP_READ
新连接NioEventLoop的分配和selector注册
- 有上方截图可知,AbstractNioMessageChannel创建客户端channel后,遍历每一条客户端连接,调用服务端的pipeline
- 服务端channel在初始化时,为pipeline添加一个默认的handler:ServerBootstrapAcceptor处理新连接(见下图红色标记)
- 下图是ServerBootstrapAcceptor#channelRead
3.1 childOptions:底层tcp读写相关的参数
3.2 childAttrs:在客户端channel绑定自定义属性,例如密钥
3.3 通过childGroup.register可以跟到chooser.next()拿到NioEventLoop
3.4 将Selector注册到底层javaChannel,将NioSocketChannel附属上去(attachment),然后不关心任何事件(和服务端类似)
NioSocketChannel读事件的注册
- 在上面selector注册后会调用pipeline的channelActive方法
1.1 关注下方截图的红色标记部分:readIfIsAutoRead(),最终会调用AbstractNioChannel#doBeginRead,进行读事件的注册