Netty核心组件详解
Posted 赵广陆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty核心组件详解相关的知识,希望对你有一定的参考价值。
目录
通过前⾯的文章,我们对Netty的整体开发有了初步的了解,在Netty中有⼀些核⼼组件,我们必须对其要有深刻的理解,下⾯我们⼀⼀来了解下。
Netty快速入门
Netty快速入门RPC项目
1 Channel
Channel可以理解为是socket连接,在客户端与服务端连接的时候就会建⽴⼀个Channel,它负责基本的IO操作,⽐如:bind()、connect(),read(),write() 等。
Netty 的 Channel 接⼝所提供的 API,⼤⼤地降低了直接使⽤ Socket 类的复杂性。不同协议、不同的阻塞类型的连接都有不同的 Channel 类型与之对应,常⽤的 Channel 类型:
NiosocketChannel,NIO的客户端 TCP Socket 连接。
NioServerSocketChannel,NIO的服务器端 TCP Socket 连接。
NioDatagramChannel, UDP 连接。
NioSctpChannel,客户端 Sctp 连接。
NioSctpServerChannel,Sctp 服务器端连接,这些通道涵盖了 UDP 和 TCP ⽹络 IO 以及⽂件IO。
2 EventLoop、EventLoopGroup
有了 Channel 连接服务,连接之间可以消息流动。如果服务器发出的消息称作“出站”消息,服务器接受的消息称作“⼊站”消息。那么消息的“出站”/“⼊站”就会产⽣事件(Event)。
例如:连接已激活;数据读取;⽤户事件;异常事件;打开链接;关闭链接等等。
有了事件,就需要⼀个机制去监控和协调事件,这个机制(组件)就是EventLoop。
在 Netty 中每个 Channel 都会被分配到⼀个 EventLoop。⼀个 EventLoop 可以服务于多个 Channel。每个 EventLoop 会占⽤⼀个 Thread,同时这个 Thread 会处理 EventLoop 上⾯发⽣的所有 IO 操作和事件。
EventLoopGroup 是⽤来⽣成 EventLoop 的,在前⾯的例⼦中,第⼀⾏代码就是 newNioEventLoopGroup();
// 主线程,不处理任何业务逻辑,只是接收客户的连接请求
EventLoopGroup boss = new NioEventLoopGroup(1);
// ⼯作线程,线程数默认是:cpu*2
EventLoopGroup worker = new NioEventLoopGroup();
如果没有指定线程数⼤⼩,默认线程数为:cpu核数*2,源码如下:
static
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors()
* 2)); //可⽤cpu核数 * 2
if (logger.isDebugEnabled())
logger.debug("-Dio.netty.eventLoopThreads: ",
DEFAULT_EVENT_LOOP_THREADS);
上图关系为:
⼀个 EventLoopGroup 包含⼀个或者多个 EventLoop;
⼀个 EventLoop 在它的⽣命周期内只和⼀个 Thread 绑定;
所有由 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理;
⼀个 Channel 在它的⽣命周期内只注册于⼀个 EventLoop;
⼀个 EventLoop 可能会被分配给⼀个或多个 Channel。
3 ChannelHandler
ChannelHandler对使⽤者⽽⾔,可以说是最重要的组件了,因为对于数据的⼊站和出站的业务逻辑的编写都是在ChannelHandler中完成的。
在前⾯的例⼦中,MyChannelHandler就是实现了channelRead⽅法,获取到客户端传来的数据。
对于数据的出站和⼊站,有着不同的ChannelHandler类型与之对应:
ChannelInboundHandler ⼊站事件处理器
ChannelOutBoundHandler 出站事件处理器
接⼝继承关系如下:
ChannelHandlerAdapter提供了⼀些⽅法的默认实现,可减少⽤户对于ChannelHandler的编写。
ChannelInboundHandlerAdapter 与 SimpleChannelInboundHandler的区别:
在服务端编写ChannelHandler时继承的是ChannelInboundHandlerAdapter
在客户端编写ChannelHandler时继承的是SimpleChannelInboundHandler
两者的区别在于,前者不会释放消息数据的引⽤,⽽后者会释放消息数据的引⽤。
4 ChannelPipeline
在Channel的数据传递过程中,对应着有很多的业务逻辑需要处理,⽐如:编码解码处理、读写操作等,那么对于每种业务逻辑实现都需要有个ChannelHandler完成,也就意味着,⼀个Channel对应着多个ChannelHandler,多个ChannelHandler如何去管理它们,它们的执⾏顺序⼜该是怎么样的,这就需要ChannelPipeline进⾏管理了。
⼀个Channel包含了⼀个ChannelPipeline,⽽ChannelPipeline中维护了⼀个ChannelHandler的列表。
ChannelHandler与Channel和ChannelPipeline之间的映射关系,由ChannelHandlerContext进⾏维护。
它们关系如下:
ChannelHandler按照加⼊的顺序会组成⼀个双向链表,⼊站事件从链表的head往后传递到最后⼀个
ChannelHandler,出站事件从链表的tail向前传递,直到最后⼀个ChannelHandler,两种类型的
ChannelHandler相互不会影响。
5 Bootstrap
Bootstrap是引导的意思,它的作⽤是配置整个Netty程序,将各个组件都串起来,最后绑定端⼝、启动Netty服务。
Netty中提供了2种类型的引导类,⼀种⽤于客户端(Bootstrap),⽽另⼀种(ServerBootstrap)⽤于服务器。
它们的区别在于:
ServerBootstrap 将绑定到⼀个端⼝,因为服务器必须要监听连接,⽽ Bootstrap 则是由想要连接到远程节点的客户端应⽤程序所使⽤的。
引导⼀个客户端只需要⼀个EventLoopGroup,但是⼀个ServerBootstrap则需要两个。
因为服务器需要两组不同的 Channel第⼀组将只包含⼀个 ServerChannel,代表服务器⾃身的已绑定到某个本地端⼝的正在监听的套接字。
第⼆组将包含所有已创建的⽤来处理传⼊客户端连接。
与ServerChannel相关联的EventLoopGroup 将分配⼀个负责为传⼊连接请求创建 Channel 的
EventLoop。⼀旦连接被接受,第⼆个 EventLoopGroup 就会给它的 Channel 分配⼀个 EventLoop。
6 Future
Future提供了⼀种在操作完成时通知应⽤程序的⽅式。这个对象可以看作是⼀个异步操作的结果的占位符,它将在未来的某个时刻完成,并提供对其结果的访问。JDK 预置了 interface java.util.concurrent.Future,但是其所提供的实现,只允许⼿动检查对应的操作是否已经完成,或者⼀直阻塞直到它完成。这是⾮常繁琐的,所以 Netty 提供了它⾃⼰的实现——ChannelFuture,⽤于在执⾏异步操作的时候使⽤。
ChannelFuture提供了⼏种额外的⽅法,这些⽅法使得我们能够注册⼀个或者多个ChannelFutureListener实例。
监听器的回调⽅法operationComplete(),将会在对应的 操作完成时被调⽤ 。然后监听器可以判断该操作是成功地完成了还是出错了。
每个 Netty 的出站 I/O 操作都将返回⼀个 ChannelFuture,也就是说,它们都不会阻塞。 所以说,Netty完全是异步和事件驱动的。
上图是 serverBootstrap.bind(port) ⽅法底层的逻辑实现。
7 小结
通过以上图将Netty中的核⼼组件串起来。
以上是关于Netty核心组件详解的主要内容,如果未能解决你的问题,请参考以下文章