Netty Server端代码简单分析(转)

Netty源码简单分析:
Netty整体架构清晰的分为两部分:
1. ChannelFactory:主要负责生产网络通信相关的Channel和ChannelSink实例,NIO Server端一般使用NioServerSocketChannelFactory,用户也可以定制自己的ChannelFactory。
2. ChannelPipelineFactory主要用来对传输数据的处理,由于对数据的处理属于业务相关,用户应自己实现ChannelPipelineFactory,然后往ChannelPipelineFactory添加自定义的Handler

Netty服务端启动步骤:

// 构造一个服务端Bootstrap实例,并通过构造方法指定一个ChannelFactory实现
// 其中后两个参数分别是BOSS和WORK的线程池
Bootstrap serverBootstrap = new ServerBootstrap(
        new NioServerSocketChannelFactory(
                Executors.newCachedThreadPool(), 
                Executors.newCachedThreadPool()));

// 注册用户自己实现的ChannelPipelineFactory
serverBootstrap.setPipelineFactory(this.pipelineFactory);

// 调用bind等待客户端来连接
((ServerBootstrap) serverBootstrap).bind(socketAddress);



Netty提供NIO与BIO两种模式,我们主要关心NIO的模式:
NIO处理方式:
1.Netty用一个BOSS线程去处理客户端的接入,创建Channel
2.从WORK线程池(WORK线程数量默认为cpu cores的2倍)拿出一个WORK线程交给BOSS创建好的Channel实例(Channel实例持有java网络对象)
3.WORK线程进行数据读入(读到ChannelBuffer)
4.接着触发相应的事件传递给ChannelPipeline进行业务处理(ChannelPipeline中包含一系列用户自定义的ChannelHandler组成的链)

有一点要注意的是,执行整个ChannelHandler链这个过程是串行的,如果业务逻辑(比如DB操作)比较耗时,会导致WORK线程长时间被占用得不到释放,最终影响整个服务端的并发处理能力,所以一般我们通过ExecutionHandler线程池来异步处理ChannelHandler调用链,使得WORK线程经过ExecutionHandler时得到释放。
要解决这个问题增加下面代码即可:

ExecutionHandler executionHandler =
        new ExecutionHandler(
                new OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576));
public ChannelPipeline getPipeline() {
        return Channels.pipeline(
                                new DatabaseGatewayProtocolEncoder(),
                                new DatabaseGatewayProtocolDecoder(),
                                executionHandler, // Must be shared
                                new DatabaseQueryingHandler());
}




Netty为ExecutionHandler提供了两种可选的线程池模型:
1) MemoryAwareThreadPoolExecutor
通过对线程池内存的使用控制,可控制Executor中待处理任务的上限(超过上限时,后续进来的任务将被阻塞),并可控制单个Channel待处理任务的上限,防止内存溢出错误;
2) OrderedMemoryAwareThreadPoolExecutor
是1)的子类。除了MemoryAwareThreadPoolExecutor 的功能之外, 它还可以保证同一Channel中处理的事件流的顺序性,这主要是控制事件在异步处理模式下可能出现的错误的事件顺序,但它并不保证同一Channel中的事件都在一个线程中执行,也没必要保证这个。
我们看下OrderedMemoryAwareThreadPoolExecutor中的注释:



处理同一个Channel的事件,是串行的方式执行的,但是同一个Channel的多个事件,可能会分布到线程中池中的多个线程去处理,不同的Channel事件可以并发处理,互相并不影响

再来看看MemoryAwareThreadPoolExecutor中的注释:



同一个Channel的事件,并不保证处理顺序,可能一个线程先处理了Channel A (Event 3),然后另一个线程才处理Channel A (Event 2),如果业务不要求保证事件的处理顺序,我认为还是尽量使用MemoryAwareThreadPoolExecutor比较好

Netty采用标准的SEDA(Staged Event-Driven Architecture) 架构
SEDA的核心思想是把一个请求处理过程分成几个Stage,不同资源消耗的Stag使用不同数量的线程来处理,Stag间使用事件驱动的异步通信模式。更进一步,在每个Stage中可以动态配置自己的线程数,在超载时降级运行或拒绝服务。

Netty所设计的事件类型,代表了网络交互的各个阶段,每个阶段发生时,会触发相应的事件并交给 ChannelPipeline进行处理。事件处理都是通过Channels类中的静态方法调用开始的。

Channels中事件流转静态方法:
1. fireChannelOpen
2. fireChannelBound
3. fireChannelConnected
4. fireMessageReceived
5. fireWriteCompleteLater
6. fireWriteComplete
7. fireChannelInterestChangedLater
8. fireChannelDisconnectedLater
9. fireChannelDisconnected
10. fireChannelUnboundLater
11. fireChannelUnbound
12. fireChannelClosedLater
13. fireChannelClosed
14. fireExceptionCaughtLater
15. fireExceptionCaught
16. fireChildChannelStateChanged

Netty将网络事件分为两种类型:
1.Upstresam:上行,主要是由网络底层反馈给Netty的,比如messageReceived、channelConnected
2.Downstream:下行,框架自己发起的,比如bind、write、connect等

Netty的ChannelHandler 分为3种类型:
1.只处理Upstream事件:实现ChannelUpstreamHandler接口
2.只处理Downstream事件:实现ChannelDownstreamHandler接口
3.同时处理Upstream和Downstream事件:同时实现ChannelUpstreamHandler和ChannelDownstreamHandler接口

ChannelPipeline维持所有ChannelHandler的有序链表,当有Upstresam或Downstream网络事件发生时,调用匹配事件类型的ChannelHandler来处理。ChannelHandler自身可以控制是否要流转到调用链中的下一个ChannelHandler(ctx.sendUpstream(e)或者ctx.sendDownstream(e)),这一样有一个好处,比如业务数据Decoder出现非法数据时不必继续流转到下一个ChannelHandler




转自: http://budairenqin.iteye.com/blog/1734111












猜你喜欢

转载自forlan.iteye.com/blog/2336923