引言
说起Netty,可能很多开发者并没有使用经验,但是我敢说,在你使用的框架中,一定有他的身影,比如(RocketMQ,redisson,dubbo,motan,Elasticsearch......
想了解更多使用Netty的项目? --> 戳这里 ), 在本文中,我们将以通俗易懂的方式,讲清楚每一个组件的职责和作用。
- 因为Netty是基于Java中NIO的封装,其本质还是使用到了Java中的NIO,所以我们先来了解下NIO的几个组件
Channel
,Selector
和ByteBuffer
1、Java NIO 三剑客 Channel
,Selector
和 ByteBuffer
1.1、 Java NIO 的 Channel
- 可以简单理解为一条连接(短连接 or 长连接),或者有人叫它通道,也没问题。
1.2、Selector选择器
- 每一个连接在创建后将会注册(
必须是实现了SelectableChannel的连接才可以注册到selector中
)到某一个选择器(selector
)上,然后轮询选择器上IO已经就绪的channel
,将某批IO就绪的channel
对应的selectKey
(在注册时候就将某个channel
与某个SelectedKey
绑定的) 添加进selectedKeys
这个set
集合中去,后续轮询则是对该selectedKeys
轮询,取出对应的selectedKey
上的channel
,然后读取channel
的数据到byteBuffer
中,接着我们可以从byteBuffer
缓冲区读取数据,并进行业务处理。
1.3、ByteBuffer缓冲区
- 本质上是一个内存块,既可以写入数据,也可以从中读取数据,其提供了三个重要的成员属性:
capacity
(容量)、position
(读写位置)、limit
(读写的限制)对于byteBuffer
,我们点到为止,知道他是干什么的就行了,对其怎么使用以及api,我们暂时不做过多展开,因为这个类的使用还是有点复杂的,一两句话也很难解释清。
1.4、(补充说明) selector可以监听的事件类型
需要说明的是,在channel注册到selector时候,会传入一个参数即ops,代表这个选择器对哪些事件感兴趣 ,直白些就是:
指定选择器要监控的IO事件类型
包括:
(1)可读:SelectionKey.OP_READ
(2)可写:SelectionKey.OP_WRITE
(3)连接:SelectionKey.OP_CONNECT
(4)接收:SelectionKey.OP_ACCEPT
什么是IO事件呢?这个概念容易混淆,这里特别说明一下。这里的IO事件不是对通道的IO操作,而是通道的某个IO操作的一种就绪状态,表示通道具备完成某个IO操作的条件。比方说,某个SocketChannel通道,完成了和对端的握手连接,则处于“连接就绪”(OP_CONNECT)状态。再比方说,某个ServerSocketChannel服务器通道,监听到一个新连接的到来,则处于“接收就绪”(OP_ACCEPT)状态。还比方说,一个有数据可读的SocketChannel通道,处于“读就绪”(OP_READ)状态;一个等待写入数据的,处于“写就绪”(OP_WRITE)状态
在讲解各个组件之前,我们需要先了解Netty是怎么工作的,以及每个环节都干了什么。这样我们从整体上有了自己的认识后,再去了解其每一个节点上的组件,我想这样会更好一些。
2、Netty线程模型(让我们知道Netty是如何工作的)
对于在上篇文章中 【Netty系列_1】Netty简介与I/O&线程模型 介绍的三种Reactor
线程模型,Netty
可以说是都支持。下面我们主要看基于Reactor
主从多线程模型 下的Netty
线程模型(在实际开发中,也是这种模型更多一些)
2.2、基于Reactor主从多线程模型下的Netty工作示意图
2.3、父子通道说明和对上图的解释
2.3.1、父子通道说明
-
在解释前,有必要说一下常用的两种Channel,即 ServerSocketChannel和SocketChannel
ServerSocketChannel对应的socket描述符是连接监听类型。连接监听类型的socket描述符,一般放在服务器端,它负责接收客户端的套接字连接,在服务器端,一个“连接监听类型”的socket描述符可以接受(Accept)成千上万的传输类的socket描述符,而SocketChannel对应的socket描述符是传输数据类型。传输类的socket描述符负责传输数据。同一条TCP的Socket传输链路(即连接),在服务器和客户端,都分别会有一个与之相对应的数据传输类型的socket描述符 这也是为什么ServerSocketChannel只在启动时创建一次,而SocketChannel则是新来个连接,就需要新建一个SocketChannel(在做Netty调优时候,我们有一招就是扩大系统(例如Linux)的文件描述符数量,其实这里的文件描述符,就是传输类型的文件描述符)
在Netty中,将有接收关系的ServerSocketChannel和SocketChannel,叫作父子通道。其中,NioServerSocketChannel负责服务器连接监听和接收,也叫父通道(Parent Channel)。而NioSocketChannel传输类通道,叫做子通道(Child Channel)
2.3.2、对上图的解释(重要)
boss group
用于监听客户端连接,如果轮询到父通道(ServerSocketChannel
)中有接收就绪
即accept
类的IO 事件,那么将会添加该父通道所绑定的selectKey到selectedKeys
中,并通过while
循环对selectedKeys
进行轮询,只要selectedKeys
有元素,那么就调用processSelectedKeys
方法,之后将其添加到taskQueue
队列,然后执行runAllTasks
并创建子通道(SocketChannel
)并且将创建好的SocketChannel
传递并注册到worker group
的某个NioEventLoop
的selector
中&#x