netty

Netty 框架总结「ChannelHandler 及 Even

2017-05-05  本文已影响412人  雪中亮

学习了一段时间的 Netty,将重点与学习心得总结如下,本文主要总结ChannelHandler 及 EventLoop 的知识点和基本用法,本文章节排序参照《Netty in Action》的章节排序。

以下内容主要参考「并发编程网」的 《Netty in Action》中文版 以及《Netty in Action》原版图书,辅助参考 Essential Netty in Action 《Netty 实战(精髓)》 以及 Netty 官网的 Netty 4.1 JavaDoc

6. ChannelHandler 和 ChannelPipeline

一个 Channel 正常的生命周期如下图所示。随着状态发生变化,相应的 event 产生。这些 event 被转发到 ChannelPipeline 中的 ChannelHandler 来采取相应的操作。

Channel状态模型

6.1 ChannelHandler

ChannelHandler 有两个重要的子接口:

6.1.1 ChannelInboundHandler

下表列出接口 ChannelInboundHandler 的方法。当收到数据或相关 Channel 的状态改变时,这些方法被调用,这些方法和Channel的生命周期密切相关

方法 描述
channelRegistered 当一个Channel注册到EventLoop上,可以处理I/O时被调用
channelUnregistered 当一个Channel从它的EventLoop上解除注册,不再处理I/O时被调用
channelActive 当Channel变成活跃状态时被调用;Channel是连接/绑定、就绪的
channelInactive 当Channel离开活跃状态,不再连接到某个远端时被调用
channelReadComplete 当Channel上的某个读操作完成时被调用
channelRead 当从Channel中读数据时被调用

6.1.2 ChannelOutboundHandler

输出的操作和数据由 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 写数据到远端

6.1.3 ChannelHandler 适配器类

ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 这两个适配器类分别提供了 ChannelInboundHandler 和 ChannelOutboundHandler 的基本实现,它们继承了共同的父接口 ChannelHandler 的方法,扩展了抽象类 ChannelHandlerAdapter。

ChannelHandlerAdapter类层级关系

6.1.4 ChannelFuture 和 ChannelPromise

6.1.5 释放资源

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 关闭时,会被自动释放,用户不用管。

6.2 ChannelPipeline 接口

6.2.1 ChannelHandlerContext

ChannelPipeline 和 ChannelHandlers

「本节参考」 第六章 ChannelHandler 和 ChannelPipeline

7. EventLoop 和 EventLoopGroup

7.1 Java 基本的线程池模式

7.2 EventLoop「事件循环」

task (Runnable或Callable) 可以直接提交到 EventLoop 实现即刻或者延后执行。根据配置和可用的CPU核,可以创建多个 EventLoop 来优化资源利用。

一个 event 的本质决定了它将如何被处理;它可能从网络协议栈传送数据到你的应用,或者反过来,或者做一些完全不一样的事情。但是 event 处理逻辑必须足够通用和灵活,来对付所有可能的情况。

所以,在 Netty 4,所有的 I/O 操作和 event 都是由分配给 EventLoop 的那一个 Thread 来处理的。Netty 4 采用的线程模型,在同一个线程的 EventLoop 中处理所有发生的事。

7.3 EventLoopGroup

针对非阻塞传输的EventLoop分配

为 Channel 的 I/O 和 event 提供服务的 EventLoop 都包含在一个 EventLoopGroup 中。EventLoop 创建和分配的方式根据传输实现的不同而有所不同。

异步实现只用了很少几个 EventLoop(和它们关联的线程),在目前 Netty 的模型中,这几个 EventLoop 被所有 Channel 共享。这让很多 Channel 被最少数量的线程服务,而不是每个 Channel 分配一个线程。

EventLoopGroup 负责分配一个 EventLoop 到每个新创建的 Channel。在目前的实现中,采用循环 (round-robin) 策略可以满足一个平衡的分配,同一个 Eventloop 还可能会被分配到多个 Channel。

「本节参考」 第七章 EventLoop和线程模型

参考链接

  1. 《Netty in Action》中文版
  2. Essential Netty in Action 《Netty 实战(精髓)》
  3. Netty 4.1 JavaDoc
上一篇下一篇

猜你喜欢

热点阅读