Netty的核心组件
Netty的主要构件块:
(1)、Channel
(2)、回调
(3)、Future
(4)、事件和ChannelHandler
这些构建块代表了不同类型的构造:资源、逻辑以及通知。
Channel
Channel是Java NIO的一个基本构造。它代表一个实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的I/O操作的程序组件)的开放连接,如读操作和写操作。
目前可以把Channel看作是传入或者传出数据的载体。因此,它可以被打开或者被关闭,连接或者断开连接。
回调
一个回调其实就是一个方法,一个指向已经被提供给另外一个方法的方法引用。这使得后者可以在适当的时候调用前者。Netty在内部使用了回调来处理事件:当一个回调被触发时,相关的事件可以被一个interfaceChannelHandler的实现处理。当一个新的连接已经被建立时,ChannelHandler的channelActive()回调方法将会被调用,并将打印出一条信息。
Future
Future提供了另一种在操作完成时通知应用程序的方式。这个对象可以看作是一个异步操作的结果的占位符;它将在未来的某个时刻完成,并提供对其结果的访问。
每个Netty的出站I/O操作都将返回一个ChannelFuture;也就是说,它们都不会阻塞。Netty完全是异步和事件驱动的。
事件和ChannelHandler
Netty使用不同的事件来通知状态的改变或者是操作的状态。
Netty是一个网络框架,所以事件都是按照入站和出站数据流的相关性进行分类的。
(1)、连接已被激活或者连接失活;
(2)、数据读取;
(3)、用户事件;
(4)、错误事件;
出站事件是未来将会触发的某个动作的操作结果,这些动作包括:
(1)、打开或者关闭到远程节点的连接;
(2)、将数据写到或者冲刷到套接字;
每个事件都可以被分发到给ChannelHandler类中某个用户实现的方法。
Netty的组件和设计
Netty解决了两个相应的关注领域,可以将其大致标记为技术的和体系结构的。首先,它的基于Java NIO的异步的和事件驱动的实现,保证了高负载下应用程序性能的最大化和可伸缩性。其次,Netty也包含了一组设计模式,将应用程序逻辑从网络层解耦,简化了开发过程,同时也最大限度地提高了可测试性、模块化以及代码的可重用性。
Channel接口
基本的I/O操作依赖于底层网络传输所提供的原语。在基于Java网络编程中,其基本的构造是class Socket。Netty的channel接口所提供的API,大大降低了直接使用Socket类的复杂性。此外,Channel也是拥有许多预定义的、专门化实现的广泛类层次结构的根。
EventLoop接口
EventLoop定义了Netty的核心抽象,用于处理连接的生命周期中所发生的事件。
(1)、一个EventLoopGroup包含一个或者多个EventLoop;
(2)、一个EventLoop在它的生命周期内只和一个Thread绑定;
(3)、所有由EventLoop处理的I/O事件都将在它专有的Thread上被处理;
(4)、一个Channel在它的生命周期内只注册于一个EventLoop;
(5)、一个EventLoop可能会被分配给一个或者多个Channel;
注意,在这种设计中,一个给定的Channel的I/O操作都是由相同的Thread执行的,实际上消除了对于同步的需要。
ChannelFuture接口
Netty中所有的I/O操作都是异步的。因为一个操作可能不会立即返回,所有我们需要一种用于在之后的某个时间点确定其结果的方法。为此,Netty提供了ChannelFuture接口,其addListener()方法注册了一个ChannelFutureListener,以便在某个操作完成时得到通知。
ChannelHandler接口
从应用程序开发人员角度来看,Netty的主要组件是ChannelHandler,它充当了所有处理入站和出站数据的应用程序逻辑的容器。这是可行的,因为ChannelHandler的方法是由网络事件触发的。事实上,ChannelHandler可专门用于几乎任何类型的动作,例如将数据从一种格式转换为另外一种格式,或者处理转换过程中所抛出的异常。
ChannelPipeline接口
ChannelPipeline为ChannelHandler链提供了容器,并定义了用于在该链上传播入站和出站事件流的API。当Channel被创建时,它会被自动地分配到它专属的ChannelPipeline。
ChannelHandler安装到ChannelPipeline中的过程如下所示:
(1)、一个ChannelInitializer的实现被注册到了ServerBootstrap中。
(2)、当ChannelInitializer.initChannel()方法被调用时,ChannelInitializer将在ChannelPipeline中安装一组自定义的ChannelHandler。
(3)、ChannelInitializer将它自己从ChannelPipeline中移除。
使得事件流经ChannelPipeline是ChannelHandler的工作,它们是在应用程序的初始化或者引导阶段被安装的。这些对象接收事件、执行它们所实现的处理逻辑,并将数据传递给链中的下一个ChannelHandler。它们的执行顺序是由它们被添加的顺序所决定的。实际上被我们称为ChannelPipeline的是这些ChannelHandler的编排顺序。
如果一个消息或者任何其他的入站事件被读取,那么它会从ChannelPipeline的头部开始流动,并被传递给第一个ChannelInboundHandler。这个ChannelHandler不一定会实际地修改数据,具体取决于它的具体功能,在这之后,数据将会被传递给链中的下一个ChannelInboundHandler。最终,数据将会到达ChannelPipeline的尾端,届时,所有的处理就都结束了。
数据的出站运动在概念上也是一样的。在这种情况下,数据将从ChannelOutboundHandler链的尾端开始流动,直到它到达链的头部位置。在这之后,出站数据将会到达网络传输层,这里显示为Socket。通常情况下,这将会触发一个写操作。
当ChannelHandler被添加到ChannelPipeline时,它将会被分配一个ChannelHandlerContext,其代表了ChannelHandler和ChannelPipeline之间的绑定。虽然这个对象可以被用于获取底层的Channel,但是它主要还是被用于写出站数据。
在Netty中,有两种发送消息的方式。可以直接写到Channel中,也可以写到和ChannelHandler相关联的ChannelHandlerContext对象中。前一种方式将会导致消息从ChannelPipeline的尾端开始流动,而后者将会导致消息从ChannelPipeline中的下一个ChannelHandler开始流动。
NIO-非阻塞I/O
NIO提供了一个所有I/O操作的异步实现。它利用了自NIO子系统被引入JDK1.4时候可用的基于选择器的API。
选择器背后的基本概念是充当一个注册表,在那里你可以将请求在Channel的状态发生变化时得到通知。可能的状态变化有:
- 新的Channel已被接受并且就绪。
- Channel连接已经完成。
- Channel有已经就绪的可提供读取的数据。
- Channel可用于写数据
选择器运行在一个检查状态变化并对其做出相应响应的线程上,在应用程序对状态的改变做出响应之后,选择器将会被重置,并重复这个过程。
名称 | 描述 |
---|---|
OP_ACCEPT | 请求在接受新连接并创建Channel时获得通知 |
OP_CONNECT | 请求在建立一个连接时获得通知 |
OP_READ | 请求当数据已经就绪,可以从Channel中读取时获得通知 |
OP_WRITE | 请求当可以向Channel中写更多的数据时获得通知。这处理了套接字缓冲区被完整填满时的情况,这种情况通常发生在数据的发送速度比远程节点可处理的速度更快的时候 |