「博客搬家」 原地址: 简书 原发表时间: 2017-05-05html
学习了一段时间的 Netty,将重点与学习心得总结以下,本文主要总结ChannelHandler 及 EventLoop 的知识点和基本用法,本文章节排序参照《Netty in Action》的章节排序。编程
如下内容主要参考「并发编程网」的 《Netty in Action》中文版 以及《Netty in Action》原版图书,辅助参考 Essential Netty in Action 《Netty 实战(精髓)》 以及 Netty 官网的 Netty 4.1 JavaDoc 。api
一个 Channel 正常的生命周期以下图所示。随着状态发生变化,相应的 event 产生。这些 event 被转发到 ChannelPipeline 中的 ChannelHandler 来采起相应的操做。promise
ChannelHandler 有两个重要的子接口:bash
下表列出接口 ChannelInboundHandler 的方法。当收到数据或相关 Channel 的状态改变时,这些方法被调用,这些方法和Channel的生命周期密切相关。网络
方法 | 描述 |
---|---|
channelRegistered | 当一个Channel注册到EventLoop上,能够处理I/O时被调用 |
channelUnregistered | 当一个Channel从它的EventLoop上解除注册,再也不处理I/O时被调用 |
channelActive | 当Channel变成活跃状态时被调用;Channel是链接/绑定、就绪的 |
channelInactive | 当Channel离开活跃状态,再也不链接到某个远端时被调用 |
channelReadComplete | 当Channel上的某个读操做完成时被调用 |
channelRead | 当从Channel中读数据时被调用 |
输出的操做和数据由 ChannelOutBoundHandler 处理。它的方法能够被 Channel,ChannelPipeline 和 ChannelHandlerContext 调用,子接口 ChannelOutboundHandler 的主要方法以下:并发
方法 | 描述 |
---|---|
bind(ChannelHandlerContext,SocketAddress,ChannelPromise) | 请求绑定 Channel 到一个本地地址 |
connect(ChannelHandlerContext, SocketAddress,SocketAddress,ChannelPromise) | 请求链接 Channel 到远端 |
disconnect(ChannelHandlerContext, ChannelPromise) | 请求从远端断开 Channel |
close(ChannelHandlerContext,ChannelPromise) | 请求关闭 Channel |
deregister(ChannelHandlerContext, ChannelPromise) | 请求 Channel 从它的 EventLoop 上解除注册 |
read(ChannelHandlerContext) | 请求从 Channel 中读更多的数据 |
flush(ChannelHandlerContext) | 请求经过 Channel 刷队列数据到远端 |
write(ChannelHandlerContext,Object, ChannelPromise) | 请求经过 Channel 写数据到远端 |
ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 这两个适配器类分别提供了 ChannelInboundHandler 和 ChannelOutboundHandler 的基本实现,它们继承了共同的父接口 ChannelHandler 的方法,扩展了抽象类 ChannelHandlerAdapter。异步
ChannelHandlerAdapter 提供了工具方法 isSharable()。若是类实现带 @Sharable 注解,那么这个方法就会返回 true,意味着这个对象能够被添加到多个 ChannelPipeline 中。ide
ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 中的方法调用相关 ChannelHandlerContext 中的等效方法,所以将事件转发到管道中的下一个ChannelHandler。工具
1. 输入方向「Inbound」 当一个 ChannelInboundHandler 实现类重写 channelRead() 方法时,它要负责释放 ByteBuf 相关的内存。可以使用 Netty 提供的工具方法:
ReferenceCountUtil.release(「ByteBuf 的对象」)
复制代码
更简单的,可以使用子类 SimpleChannelInboundHandler ,一条消息在被 ChannelRead0() 读取后,会被自动释放资源,此时任何对消息的引用都会变成无效,因此不能保存这些引用待后来使用。
2. 输出方向「Outbound」 在输出方向,若是处理一个 write() 操做而且丢弃一条消息(没有写入 Channel),就应该负责释放这条消息。
@ChannelHandler.Sharable public
class DiscardOutboundHandler extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
ReferenceCountUtil.release(msg); //使用 ReferenceCountUtil.release(...) 释放资源
promise.setSuccess(); //通知 ChannelPromise 数据已经被处理
}
复制代码
若是一个消息被“消费”或者丢弃,没有送到 ChannelPipeline 中的下一个 ChannelOutboundHandler,用户就要负责调用 ReferenceCountUtil.release()。若是消息到达了真正的传输层,在它被写到 Socket 中或者 Channel 关闭时,会被自动释放,用户不用管。
每一个新建立的 Channel 都会分配一个新的 ChannelPipeline,Channel 不能够更换或解除当前的 ChannelPipeline,在 Netty 组件的整个生命周期中这个关系是固定的。
一个 ChannelPipeline 可当作是一串 ChannelHandler 实例,拦截穿过 Channel 的输入输出 event。
根据来源,一个 event 能够被一个 ChannelInboundHandler 或 ChannelOutboundHandler 处理。接下来,经过调用 ChannelHandlerContext 的方法,event 会被转发到下一个同类型的 handler。
经过 ChannelHandlerContext,一个 handler 能够通知 ChannelPipeline 中的下一个ChannelHandler,甚至动态改动下一个ChannelHandler 所属的 ChannelPipeline。
ChannelPipeline 主要由一系列 ChannelHandler 组成的。ChannelPipeline 提供在 ChannelPipeline 中传送 event 的方法。
ChannelHandlerContext 的一些方法和其余类(Channel 和 ChannelPipeline)的方法名字类似,可是 ChannelHandlerContext 的方法采用了更短的 event 传递路程。咱们应该尽量利用这一点来实现最好的性能。
若是你在 Channel 或者 ChannelPipeline 实例上调用这些方法,它们的调用会穿过整个 pipeline。而在 ChannelHandlerContext 上调用的一样的方法,仅仅从当前 ChannelHandler 开始,走到 pipeline 中下一个能够处理这个 event 的 ChannelHandler。
「本节参考」 第六章 ChannelHandler 和 ChannelPipeline
task (Runnable或Callable) 能够直接提交到 EventLoop 实现即刻或者延后执行。根据配置和可用的CPU核,能够建立多个 EventLoop 来优化资源利用。
一个 event 的本质决定了它将如何被处理;它可能从网络协议栈传送数据到你的应用,或者反过来,或者作一些彻底不同的事情。可是 event 处理逻辑必须足够通用和灵活,来对付全部可能的状况。
因此,在 Netty 4,全部的 I/O 操做和 event 都是由分配给 EventLoop 的那一个 Thread 来处理的。Netty 4 采用的线程模型,在同一个线程的 EventLoop 中处理全部发生的事。
为 Channel 的 I/O 和 event 提供服务的 EventLoop 都包含在一个 EventLoopGroup 中。EventLoop 建立和分配的方式根据传输实现的不一样而有所不一样。
异步实现只用了不多几个 EventLoop(和它们关联的线程),在目前 Netty 的模型中,这几个 EventLoop 被全部 Channel 共享。这让不少 Channel 被最少数量的线程服务,而不是每一个 Channel 分配一个线程。
EventLoopGroup 负责分配一个 EventLoop 到每一个新建立的 Channel。在目前的实现中,采用循环 (round-robin) 策略能够知足一个平衡的分配,同一个 Eventloop 还可能会被分配到多个 Channel。
「本节参考」 第七章 EventLoop和线程模型