Netty使用不同的事件来通知我们状态的改变或者操作的状态,这使得我们能够基于已经发生的事件来触发适当的动作,这些动作可能有:
- 记录日志
- 数据转换
- 流控制
- 应用程序逻辑
Netty是一个网络编程框架,所以事件是按照它们与入站或出站数据流的相关性进行分类的。可能由入站数据或者相关状态更改而触发的事件包括:
- 链接已激活或者链接失活
- 数据读取
- 用户事件
- 错误事件
出站事件是未来将会触发的某个动作的操作结果,这些动作包括:
- 打开或者关闭远程节点的链接
- 将数据写到或者冲刷到套接字
在ChannelHandler内部,也是用了事件和Future(想看一看源码是如何实现这部分的)。
Netty的异步编程模型是建立在Future和回调的概念之上的,而将事件派发到ChannelHandler的方法则发生在更深的层次上。
Netty通过触发事件将Selector从应用程序中抽象出来,消除了所有本来需要手动编写的派发代码。在内部,将会为每个Channel分配一个EventLoop,用于处理所有事件(EventLoop用于处理所有事件),包括:
- 注册感兴趣的事件(这部分代码我怀疑被Bootstrap、ServerBootstrap给隐藏了)
- 将事件派发给ChannelHandler
- 安排进一步的动作
EventLoop本身只由一个线程驱动,其处理了一个Channel的所有IO事件,并且在该Eventloop的整个生命周期内都不会改变。
使得事件流经过ChannelPipeline是ChannelHandler的工作,它们是在应用程序的初始化或者引导阶段被安排的。这些对象接受事件、执行它们所实现的处理逻辑,并将数据传递给链中的下一个ChannelHandler。
当ChannelHandler被添加到ChannelPipeline时,它将会被分配一个ChannelHandlerContext,其代表了ChannelHandler和ChannelPipeline之间的绑定。虽然这个对象可以被用于获取底层的Channel,但是它主要还是被用于写出数据。
在Netty中,有两种发送消息的方式。可以直接写到Channel中,也可以写到和ChannelHandler相关联的ChannelHandlerContext对象中。前一种方式会导致消息从ChannelPipeline的尾端开始流动,而后者将导致消息从ChannelPipeline中的下一个ChannelHandler开始流动(擦,我不知道这个下一个该如何理解,如果入站时写,会调用链中的下一个入站ChannelHandler么)。
ChannelHandler的典型用途包括:
- 将数据从一种格式转换成另一种格式(好理解)
- 提供异常的通知(好理解)
- 提供Channel变为活动或者非活动的通知(通知到开发人员)
- 提供当Cahnnel注册到EventLoop或者从EventLoop注销时的通知(通知到开发人员)
- 提供有关用户自定义事件的通知(目前未接触)
可以根据需要通过添加或者移除ChannelHandler实例来修改ChannelPipeline。通过利用Netty的这项能力可以构建出高度灵活的应用程序。例如,每当STARTTLS协议被请求时,你可以简单地通过想ChannelPipeline添加一个适当的ChannelHandler来按需支持STARTTLS。
(我在想该技术另一个层面的应用:我们在应用程序和服务端协商后再初始化ChannelPipeline,这样就可以按需加载ChannelHandler,我觉得这个想法非常非常的棒)
Netty提供了一种额外的传输,可以将一组ChannelHandler作为帮助器类嵌入到其他的ChannelHandler内部,通过这种方式,可以扩展一个ChannelHandler的功能(不是很理解这个在讲什么)。Embedded传输的关键是一个被称为EmbeddedChannel的具体的Channel实现。