没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论











Java NIO Netty 实现原理浅析
本文将主要分析 Netty 实现方面的东西,由于精力有限,本人并没有对其源码做了极细 致的研 究。如果下面的内容有错误或不
严谨的地方,也请指正和谅解。对于 Netty 使用者来说,Netty 提供了几个典型的 example,并有详尽的 API doc 和 guide
doc,本文的一些内容及图示也来自于 Netty 的文档,特此致谢。
1、总体结构
先放上一张漂亮的 Netty 总体结构图,下面的内容也主要围绕该图上的一些核心功能做分析, 但对如 Container Integration 及
Security Support 等高级可选功能,本文不予分析。
2、网络模型
Netty 是典型的 Reactor 模型结构,关于 Reactor 的详尽阐释,可参考 POSA2,这里不做概念性的解释。而应用 JavaNIO 构建
Reactor 模式,Doug Lea(就是那位让人无限景仰的大爷)在“Scalable IO inJava”中给了很好的阐述。这里截取其 PPT 中经
典的图例说明 Reactor 模式的典型实现:
1、这是最简单的单 Reactor 单线程模型。Reactor 线程是个多面手,负责多路分离套接字,Accept 新连接,并分派请求到处
理器链中。 该模型 适用于处理器链中业务处理组件能快速完成的场景。不过,这种单线程模型不能充分利用多核资源,所以
实际使用的不多。

2、相比上一种模型,该模型在处理器链部分采用了多线程(线程池),也是后端程序常用的模型。
3、 第三种模型比起第二种模型,是将 Reactor 分成两部分,mainReactor 负责监听 server socket,accept 新连接,并将建立
的 socket 分派给 subReactor。subReactor 负责多路分离已连接的 socket,读写网 络数据,对业务处理功能,其扔给 worker
线程池完成。通常,subReactor 个数上可与 CPU 个数等同。

说完 Reacotr 模型的三种形式,那么 Netty 是哪种呢?其实,我还有一种 Reactor 模型的变种没说,那就是去掉线程池的第三
种形式的变 种,这也 是 Netty NIO 的默认模式。在实现上,Netty 中的 Boss 类充当 mainReactor,NioWorker 类充当
subReactor(默认 NioWorker 的个数是 Runtime.getRuntime().availableProcessors())。在处理新来的请求 时,NioWorker
读完已收到的数据到 ChannelBuffer 中,之后触发 ChannelPipeline 中的 ChannelHandler 流。
Netty 是事件驱动的,可以通过 ChannelHandler 链来控制执行流向。因为 ChannelHandler 链的执行过程是在 subReactor 中
同步的,所以如果业务处理 handler 耗时长,将严重影响可支持的并发数。这种模型适合于像 Memcache 这样的应用场景,但
对需要操作数据库或者和其他模块阻塞交互的系统就不是很合适。Netty 的可扩展性非常好,而像 ChannelHandler 线程池化的
需要,可以通过在 ChannelPipeline 中添加 Netty 内置的 ChannelHandler 实现类–ExecutionHandler 实现,对使用者来说只是
添加一行代码而已。对于 ExecutionHandler 需要的线程池模型,Netty 提供了两种可 选:1)
MemoryAwareThreadPoolExecutor 可控制 Executor 中待处理任务的上限(超过上限时,后续进来的任务将被阻 塞),并可
控制单个 Channel 待处理任务的上限;2) OrderedMemoryAwareThreadPoolExecutor 是 MemoryAwareThreadPoolExecutor
的子类,它还可以保证同一 Channel 中处理的事件流的顺序性,这主要是控制事件在异步处 理模式下可能出现的错误的事件
顺序,但它并不保证同一 Channel 中的事件都在一个线程中执行(通常也没必要)。一般来 说,
OrderedMemoryAwareThreadPoolExecutor 是个很不错的选择,当然,如果有需要,也可以 DIY 一个。
3、 buffer
org.jboss.netty.buffer 包的接口及类的结构图如下:

该包核心的接口是 ChannelBuffer 和 ChannelBufferFactory,下面予以简要的介绍。
Netty 使用 ChannelBuffer 来存储并操作读写的网络数据。ChannelBuffer 除了提供和 ByteBuffer 类似的方法,还 提供了 一些
实用方法,具体可参考其 API 文档。
ChannelBuffer 的实现类有多个,这里列举其中主要的几个:
1)HeapChannelBuffer:这是 Netty 读网络数据时默认使用的 ChannelBuffer,这里的 Heap 就是 Java 堆的意 思,因为 读
SocketChannel 的数据是要经过 ByteBuffer 的,而 ByteBuffer 实际操作的就是个 byte 数组,所以 ChannelBuffer 的内部就包含
了一个 byte 数组,使得 ByteBuffer 和 ChannelBuffer 之间的转换是零拷贝方式。根据网络字 节续的不同,HeapChannelBuffer
又分为 BigEndianHeapChannelBuffer 和 LittleEndianHeapChannelBuffer,默认使用的是
BigEndianHeapChannelBuffer。Netty 在读网络 数据时使用的就是 HeapChannelBuffer,HeapChannelBuffer 是个大小固定的
buffer,为了不至于分配的 Buffer 的 大小不太合适,Netty 在分配 Buffer 时会参考上次请求需要的大小。
2)DynamicChannelBuffer:相比于 HeapChannelBuffer,DynamicChannelBuffer 可动态自适 应大 小。对于在
DecodeHandler 中的写数据操作,在数据大小未知的情况下,通常使用 DynamicChannelBuffer。
3)ByteBufferBackedChannelBuffer:这是 directBuffer,直接封装了 ByteBuffer 的 directBuffer。
对于读写网络数据的 buffer,分配策略有两种:1)通常出于简单考虑,直接分配固定大小的 buffer,缺点是,对一些应用来说
这个大小限制有 时是不 合理的,并且如果 buffer 的上限很大也会有内存上的浪费。2)针对固定大小的 buffer 缺点,就引入动
态 buffer,动态 buffer 之于固定 buffer 相当于 List 之于 Array。
buffer 的寄存策略常见的也有两种(其实是我知道的就限于此):1)在多线程(线程池) 模型下,每个线程维护自己的读写
buffer,每次处理新的请求前清空 buffer(或者在处理结束后清空),该请求的读写操作都需要在该线程中完成。 2)buffer 和
socket 绑定而与线程无关。两种方法的目的都是为了重用 buffer。
Netty 对 buffer 的处理策略是:读 请求数据时,Netty 首先读数据到新创建的固定大小的 HeapChannelBuffer 中,当
HeapChannelBuffer 满或者没有数据可读 时,调用 handler 来处理数据,这通常首先触发的是用户自定义的 DecodeHandler,
剩余19页未读,继续阅读
资源评论

- 似水流年依旧2014-07-27看了下,确实不错
- Y_0352014-07-16看了下,不错
- Kampfgruppe2013-01-05不错,不过写得比较深,对于我这种半吊子的还是有些吃力。还是感谢你的分享!

ftplove
- 粉丝: 2
- 资源: 4
上传资源 快速赚钱
我的内容管理 展开
我的资源 快来上传第一个资源
我的收益
登录查看自己的收益我的积分 登录查看自己的积分
我的C币 登录后查看C币余额
我的收藏
我的下载
下载帮助


安全验证
文档复制为VIP权益,开通VIP直接复制
