netty源代码解读-架构流程
发表于
2011 年 10 月 17 日 由
kennetty是什么?直接用官方的介绍-netty是一个异步的,基于事件的网络框架。基于它,可以快速开发高性能,易扩展的服务端和客户端程序。一开始,netty还很纯洁,是一个纯粹的NIO网络框架。时光荏苒,它现在已经支持多种网络协议了,变得愈加庞大强大了
netty 架构图
本文的目的是对netty的源代码的网络处理流程进行分析,相对比较零散,阅读者最好结合代码来看,阅读的顺序也最好基于本文的描述顺序。本文使用的netty版本是3.2.5.Final。关于netty的实际应用,可以参考本blog的另外一篇文章(利用netty快速搭建一个高性能的聊天服务器)
从一个最简单的实例开始吧。我们创建一个基于NIO的server程序,代码大致如下:
// 创建一个server实例,并且配置其SocketChannelFactory
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
// 再配置它的 pipeline factory.
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new ServerHandler());
}
});
//bind,然后开始接受新的链接到来
bootstrap.bind(new InetSocketAddress(8080));
是不是感觉so easy:) 代码注释已经给出了简单介绍。接下来进入细节。
ServerBootstrap 是一个辅助类,用来创建服务端Channel,接受客户端连接。它有两个重要的属性ChannelFactory和PipelineFactory。可以看到接下来的代码都是在配置这两个属性。看到xxFactory的命名,很自然想到是用了设计模式的工厂模式。这两个类分别是用来创建Channel实例和Pipeline实例的。
Channel 封装了nio 的 SocketChannel。提供了各种I/O操作,需要注意的是,各种操作都是异步的。每个操作函数,你都将获得一个ChannelFuture返回值,可以用它监听各种事件(比如操作结束)。
Pipeline是netty中一个重要的数据结构,是理解netty框架架构,事件机制的关键因素。Pipeline是一个链表结构,节点存储的是ChannelHandler。netty的事件触发机制是这样的:当触发某个事件时候,从pipeline中获得头节点,查看该ChannelHandler有无Override重写该事件的处理函数。如果有,就调用。然后继续查找下一个节点,重复上面的过程,直到链表尾部。
pipeline 结构图
ChannelHandler是各种事件处理操作,比如我们这里的业务逻辑类ServerHandler。我们在ChannelHandler中可以重写Override感兴趣的事件处理函数。基于这种设计架构,我们的代码可以很优雅。比如,客户端发来一条事件,触发了读相关事件,我们可以先调用解码的xxDecoder,然后再继续传递给相关的业务处理Handler。这样,就把业务逻辑跟编码解码解耦了。
这里很有必要在详细描述一下netty的事件机制。在netty中,只包括两种事件upstream 和 downstream。处理upstream时,是从pipeline的第一个节点开始,直到尾部。相反,downstream是从尾节点开始的。也可以这样理解,upstream是由网络层向netty发起的,例如收到一条消息。而downstream是netty向网络层发起的,例如write 操作
下面进入到NioServerSocketChannelFactory内部,它里面包含了boss线程序和woker线程的执行器.这样的好处是boss线程和woker线程可以方便管理了,比如统一关闭销毁.boss线程是负责处理socket连接,worker线程是负责处理io的.worker线程的数目一般设置为cpu核数*2. ChannelFactory内部还有一个称为ChannelSink的部件,我们称为”连接器”.ChannelSink用来接受和处理downstream 事件的终结点.当一个downstream 事件到达底部(也就是pipeline的头节点),会触发ChannelSink的eventSunk函数
终于到绑定操作bind了.这里比较复杂点,首先在boss的pipeline中插入一个Binder handler,用来监听channel open等事件.接着利用上面的ChannelFactory new 一个NioServerChannel.这里开始open ServerSocketChannel,然后抛出ChannelOpen事件。上面的binder handler就监听处理ChannelOpen事件了。事件处理函数主要两个操作,设置socket的相关属性和执行Channels的 bind函数。bind函数将Bound事件传递下去,直到downstream的底端,上面谈过的ChannelSink的eventSunk被触发。
在eventSunk中,真正的socket bind函数被调用。接下来就启动Boss线程,开始接受新的连接了。当一个新的连接到了,就从取得一个worker线程,由worker线程处理读写了
worker线程是整个框架的核心了,主要包括以下操作:处理boss线程推送过来的socket;处理写队列,处理各种selector事件(读写)。最新的版本对写操作做了优化,不用放入写队列,直接写,减少 selector事件的等待,提高效率。
上面的描述可能显得凌乱,最好的办法是拿源代码来进行调试,结合我的这篇blog来分析。其实netty的框架核心是nio,Doug Lea 曾经描述过各种模型,下面的模式是性能最佳的,也是netty的架构雏形
结束:因为netty的庞大,篇幅有限,描述上可能不够清晰,希望你阅读后能有收获。如果有机会,我将继续探讨netty的源代码的其他部分,也欢迎交流。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请
点击举报。