开门见山,在此我就不介绍Netty,关于Neety的介绍、用法我在此推荐一篇文章http://www.kafka0102.com/2010/06/167.html,关于Java NIO网络编程的知识请Google。
Netty版本:3.6.6
个人觉得Netty之所以高性能主要是因为它的多路I/O复用模型和零拷贝的Buffer。
Netty中有两大核心组件ChannelFactory与ChannelPipeline,可以说Netty的整个架构的核心都是靠这两大组件支撑起来的。
Netty的网络模型(ChannelFactory)
ChannelFactory有两组线程池BossPool和WorkerPool,BossPool相对上图的mainReactor负责接受连接请求,workerPool对应上图的SubReactor和ThreadPool部分,用于处理自身任务队列、读、写等操作。
BossPool工作线程接受到连接后,将此连接交给workerPool,workerPool监听并处理此连接的读写事件.
下面看一下Netty的网络模型实现原理
ChannelFactory接口
它负责创建Channel。
NioServerSocketChannelFactory类
实现自ChannelFactory接口,服务器端的Channel工厂
NioClientSocketChannelFactory类
实现自ChannelFactory接口,客户端的Channel工厂
这两个实现类中都维护了3个对象bossPool,workerPool,sink;
sink:则对应PipeLine设计模式(下面会了解到)中的sink部分;
下面是ChannelFactory的基本原理图(有些类,有些方法看不懂没事,源码分析的时候会了解到)
Thread Pipeline设计模式
关于此设计模式的思想请参看此PPT:http://febird.googlecode.com/svn-history/r515/trunk/febird/doc/multi_thread_pipeline_cn.pptx
这个PPT讲得可能有点抽象,我举个包子解释一下:
Pipeline可以看成是生产车间的一条生产流水线,原材料(数据)经过流水线中的每一道工序加工(也就是Handler)最终成为一个产品,每道工序由不同的人(Thread)干,有的工序复杂做得比较慢,有的工序简单做得比较快,但是一个产品必需等到所有工序都加工完后才能进行组装出厂(sink所干的事)。
WorkThreadPool:有了生产线流水线那谁来负责生产呢?工人。如果把工人看成是一个Thread那么一个生产车间里的所有工人组成的集合可以看成是ThreadPool。正常情况下,一个工人往往会被分配很多的原材料(数据),但一个工人每次只能加工一个原材料(假设是这样),因此必需要有一个池子(任务排队)来保证工人们正常完成工作,工人的工资一般与自己加工的产品量挂钩,如果大家都从这个池子里拿原材料加工将很有可能会导致冲突(线程同步导致的阻塞),因此厂里给每个工人按月给定一定的数量的任务(WorkThread有了自己的队列),这样每个工人各自完成各自的任务,几乎不会有竞争。
ChannelPipeline的默认实现类是DefaultChannelPipeline,各个被注册到Pipeline中的ChanelHandler都是在同一个工作线程中执行的,不会被拆分到多个线程中去,DefaultChannelPipeline中维护的是一个单向前后节点相连的链表的首尾指针(head,tail),此链表的数据结构如图:
(DefaultChanelPipeline)
当底层NIO触发“读”事件的时候将从head节点开始执行如下动作:
1、判断当前节点的Handler是否实现了ChannelUpstreamHandler接口
a)如果是则执行其handleUpstream(ctx, e)
b)否则继续向查找next直到为next为null
当代码调用channel.write方法后将从tail节点开始执行如下动作:
1、判断当前节点的Handler是否实现了ChannelDownstreamHandler接口
a)如果是则执行其handleDownstream(ctx, e)
b)否则继续查找prev直到为prev为null
c)当上面两种情况在执行的时候,如果当前节点的prev为null的时候则执行此Pipeline中sink对象的eventSunk方法做关闭、绑定、链接、写数据等操作
如图
从图中可以看出底层NIO触发read事件将触发Pipeline的Upstream直到最后的Hhandler处理完成(这里通常做解码操作)后丢弃;当用户代码调用Channel的write方法后将触发Pipeline的DownStream直到最前面的Handler处理完后(这里通常做编码操作)再执行底层Socket的write操作。
自定义的ChannelHandler可以控制是否继续执行后续的handler。这点很像Spring中的ReflectiveMethodInvocation