前言
在第一篇文章《Netty框架入门篇 - 初识Netty及第一款Netty应用程序》详细介绍了Netty的一些基础知识,并粗略的分析了Netty的一些核心组件,为了后面更好地和进一步的了解Netty,本文将详细介绍Netty的核心组件以及它们是如何通过协作来支撑整个Netty体系结构的
Channel、EventLoop和ChannelFuture
Channel、EventLoop和ChannelFuture这些类,可以被认为是Netty网络抽象的代表:
- Channel等价于网络编程中的Socket,用于网络数据传输;
- EventLoop指事件循环,用于处理连接中的事件,控制流、多线程处理、并发
- ChannelFuture指异步通知,可以看作是将来要执行的操作的结果的占位符
Channel、EventLoop、Thread以及EventLoopGroup之间的关系图(摘自《Netty In Action》):
- 一个EventLoopGroup包含一个或者多个EventLoop;
- 一个EventLoop在它的生命周期内只和与一个Thread绑定;
- 所有由EventLoop处理的I/O 事件都将在它专有的Thread上被处理;
- 一个Channel在它的生命周期内只能注册于一个EventLoop;
- 一个EventLoop可能会被分配至一个或多个Channel。
Channel
基本的I/O操作(bind()、connect()、read()和write())依赖于底层网络传输所提供的原语,在传统的网络编程中,其基本的构造是Socket,而Socket类对开发者来说并不友好,使用起来相对复杂。相对的Netty的Channel接口所提供的API,大大地降低了直接使用Socket类的复杂性。并且Netty的Channel相对于原生NIO的Channel具有如下优势:
- 在Channel接口层,采用Facade模式进行统一封装,将网络 I/O 操作、网络 I/O 相关联的其他操作封装起来,统一对外提供;
- Channel 接口的定义尽量大而全,为 SocketChannel 和 ServerSocketChannel 提供统一的视图,由不同子类实现不同的功能,公共功能在抽象父类中实现,最大程度地实现功能和接口的重用;
- 具体实现采用聚合而非包含的方式,将相关的功能类聚合在Channel中,有Channel统一负责和调度,功能实现更加灵活。
此外,Channel也是拥有许多预定义的、专门化实现的广泛类层次结构的根,主要包括:
- NioSocketChannel
- NioDatagramChannel
- NioSctpChannel
- EmbeddedChannel
Channel的生命周期状态
状态 | 描述 |
---|---|
ChannelUnregistered | Channel已经被创建,但还未注册到EventLoop |
ChannelRegistered | Channel已经被注册到EventLoop |
ChannelActive | Channel 处于活动状态(已经连接到它的远程节点),可以接收和发送数据 |
ChannelInactive | Channel没有连接到远程节点 |
Channel在其生命周期过程中,当状态发生改变时,都会生成相应的事件,而这些事件都会被传递给指定的ChannelHandler来进行处理
EventLoop
EventLoop可以称之为事件循环,它定义了Netty的核心抽象,用于处理网络连接的生命周期中所发生的事件。对于一个 EventLoop将由一个永远都不会改变的Thread驱动,同时任务可以直接提交给EventLoop,以立即执行或者调度执行。
根据配置和可用核心的不同,可能会创建多个EventLoop实例用以优化资源的使用,并且单个EventLoop可能会被指派用于服务多个Channel
Netty的EventLoop在继承了ScheduledExecutorService 的同时,只定义了一个方法parent()。在Netty 4中,所有的 I/O 操作和事件都由已经被分配给了EventLoop的那个Thread来处理
任务调度
偶尔,你将需要调度一个任务以便稍后(延迟)执行或者周期性地执行。例如,你可能想要注册一个在客户端已经连接了5分钟之后触发的任务。一个常见的用例是,发送心跳消息到远程节点,以检查连接是否仍然还活着。如果没有响应,你便知道可以关闭该Channel了
使用EventLoop调度任务
Channel channel = ctx.channel();
channel