`

Netty启动分析 (转)

    博客分类:
  • nio
 
阅读更多
见: http://blog.csdn.net/zhangzhaokun/article/details/6854494

本文主要记录下我在学习Netty源代码过程中,以org.jboss.netty.example.echo.EchoServer为例,在启动Server端的时候,Netty是如何建立Boss与NIOWorker的,以及如何建立Selector、绑定服务器端口等过程的细节,借此来学习Netty的Pipeline、ChannelHandle、Boss、NioWorker等各自的分工是怎样,它们又是如何协作的?

(基础一)Netty网络模型图


(基础二)ChannelPipeline原理



具体的Netty实现原理上可以参见《Netty实现原理浅析》,该文写得很好,我这里不再详细记述。

以下为我在代码阅读的过程记述的一些要点,先就有这些东西,后续再补充完整。

第一节:建立MainReactor和SubReactor线程池
 ServerBootstrap bootstrap = new ServerBootstrap(
                new NioServerSocketChannelFactory(
                        Executors.newCachedThreadPool(),
                        Executors.newCachedThreadPool()));


指定创建一个NioServerSocketChannelFactory(),建立一个BossExecutor的执行器,和一个workExecutor的执行器。
    同时创建一个NioServerSocketPipelineSink(workerExecutor,workCount)对象,建立cup*2个NioWorker的实例,
    这个类主要在于实现downstream已经达到末端的时候的处理事件,简单来说就是downstream的最后一个处理环节了。      

第二节:完成Client请求的ChannelHandle链的建立
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() throws Exception {
                return Channels.pipeline(new EchoServerHandler());
            }
        });


设置服务器端的ChannelPipelineFactory对象,在该工厂对象中指定使用哪一个ChannelPipeline的实现实例,在EchoServer中使用DefaultChannelPipeline来作为ChannelPipeline,同时指定ChannelPipeline中需要使用的ChannelHandle链接。 在指定ChannelPipeline的ChannelHandle的过程,对于每一个DefaultChannelPipeline的实例对象,都建立一个<name,ChannelHandle>的映射关系, 并且对于每一个ChannelHandle都关联一个DefaultChannelHandleContext对象,在该对象中指定了当前ChannelHandle的前一个、后一个ChannelHandle, ChannelPipeline中的DefaultChannelHandleContext是按照ChannelHandle1(Head:prev=null,next=ChannelHandle2)->ChannelHandle1(tail:prev=ChannelHandle1,next=null)构造ChannelHandle链的,对于downstream的事件,是从tail->head的顺序执行ChannelHandle的,所以prev为空表示downstream链的末端,next为空表示upstream链的末端,在downstream链的末端则需要执行NioServerSocketPipelineSink来处理了。

第三节:建立一个Server端启动所对应的ChannelHandle链
    bootstrap.bind(new InetSocketAddress(8080));  


关键的动作尽在此步骤中了。首先建立了队列new LinkedBlockingQueue<ChannelFuture>(),再次之后就建立了一个处理Boss的ChannelHandle实例对象Binder(), 之后再建立了一个DefaultChannelPipeline对象来作为Boss的ChannelPipeline,该ChannelPipeline中的ChannelHandle对象就只有一个Binder()的ChannelHandle了,并且该Binder对象实例只是一个处理upstream的ChannelHandle

第四节:打开服务器端Socket,发起一个Channel的Open的Upstream事件,后续将OPEN事件转换为一个Downstream的BIND事件

Channel channel = getFactory().newChannel(bossPipeline);  


利用上述第(1)步中建立的对象NioServerSocketChannelFactory来建立一个ServerSocketChannel,在创建NioServerSocketChannel对象的时候, 会调用如下代码块:
            socket = ServerSocketChannel.open();
            socket.configureBlocking(false);  

     紧接下来就是调用fireChannelOpen(this)来调用步骤(3)中建立的DefaultChannelPipeline对象,发送一个upstream事件,ChannelState为OPEN 这个是触发upstream的ChannelHandle的第一个入口,后续就会依次调用DefaultChannelHandlerContext的Next所指向的ChannelHandle的handleUpstream()方法, 对于EchoServerHandle来说,对应的handleUpstream()方法就会调用SimpleChannelsUpstreamHandle的handleUpstream()方法,对于ChannelStateEvent.OPEN事件来说, 会调用ServerBootstrap.Binder.channelOpen()方法,在Binder这个ChannelHandle处理完成之后,又会使用DefaultChannelHandleContext.sendUpstream(evt)来处理后续的链节点, 只是这里只有一个ChannelHandle,到这里就执行完毕了。 在ServerBootstrap.Binder.channelOpen()方法中有如下代码:boolean finished = futureQueue.offer(evt.getChannel().bind(localAddress));将一个ChannelState.OPEN事件转换为 一个BIND事件了,即调用NioServerSocketChannel.bind()方法,具体的实现代码在AbstractChannel.bind()方法中,实则是调用Channels.bind()方法,其中Channels类是一个工具类。

第五节:完成DownStream的BIND事件的处理

ChannelFuture future = future(channel);        
channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
                channel, future, ChannelState.BOUND, localAddress)); 


Channels.bind()方法中主要是如上代码,将ChannelState.BIND事件,构造为一个downstream事件,由Boss的ChannelPipeline中的ChannelHandle链来完成这个downstream的处理。 DefaultChannelPipeline.sendDownstream()方法会从ChannelHandle链的链尾节点开始找能够处理downstream的ChannelHandle节点来处理该事件,在这里由于ServerBootstrap.Binder只是一个 Upstream的Handle,所以没有对应的downstream的ChannelHandle节点可以处理该事件,直接就由(1)步所建立的NioServerSocketPipelineSink对象来完成downstream的末端处理,即会调用NioServerSocketPipelineSink.eventSunk()方法,再到NioServerSocketPipelineSink.handleServerSocket()方法,再又调用了NioServerSocketPipelineSink.bind()方法。

第六节:将服务器端Socket绑定到指定端口
channel.socket.socket().bind(localAddress, channel.getConfig().getBacklog());


NioServerSocketPipelineSink.bind()方法中有如上代码,完成关键的一步,即将建立了一个ServerSocketChannel,并且绑定到指定的端口

第七节:触发一个Upstream的BOUND事件
fireChannelBound(channel, channel.getLocalAddress());


NioServerSocketPipelineSink.bind()方法中有如上代码,触发一个Upstream的BOUND事件,但是遗憾的是后续并没有看到什么有特别用途的操作发生。

第八节:将服务器端Socket与selector绑定起来,完成对后续Client链接的监听

Executor bossExecutor =
                ((NioServerSocketChannelFactory) channel.getFactory()).bossExecutor;
            DeadLockProofWorker.start(
                    bossExecutor,
                    new ThreadRenamingRunnable(
                            new Boss(channel),
                            "New I/O server boss #" + id + " (" + channel + ')'));
            bossStarted = true;


这个是NioServerSocketPipelineSink.bind()方法中的一段代码,其中有一个new Boss(channel)操作,会执行一个Selector.open()动作,再继而以channel.socket.register(selector, SelectionKey.OP_ACCEPT);这个动作把刚才新建的NioServerSocketChannel与一个Selector的实例绑定起来。其中的DeadLockProofWorker.start()这个是将一个绑定的Boss线程放到(1)步骤中的bossExecutor线程池中,开始执行Boss线程。

第九节:BOSS线程轮询接收Client来链接请求

 for (;;) {
                    try {
                        if (selector.select(1000) > 0) {
                            selector.selectedKeys().clear();
                        }

                        SocketChannel acceptedSocket = channel.socket.accept();
                        if (acceptedSocket != null) {
                            registerAcceptedChannel(acceptedSocket, currentThread);
                        }
                    } catch (SocketTimeoutException e) {
                        // Thrown every second to get ClosedChannelException
                        // raised.
                    } catch (CancelledKeyException e) {
                        // Raised by accept() when the server socket was closed.
                    } catch (ClosedSelectorException e) {
                        // Raised by accept() when the server socket was closed.
                    } catch (ClosedChannelException e) {
                        // Closed as requested.
                        break;
                    } catch (Throwable e) {
                        logger.warn(
                                "Failed to accept a connection.", e);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e1) {
                            // Ignore
                        }
                    }
                }


以上代码为NioServerSocketPipelineSink.Boss.run()中的一部分,主要母的就是让selector来不断的轮询,看是否有客户端的链接过来,有的话就利用registerAcceptedChannel(acceptedSocket, currentThread);来将链接来的Socket注册到NioWorker,有NioWorker(subreactor)来完成对客户端链接的处理。

至此Netty的服务端启动完成了。
对于服务端的启动过程,Netty也是以ChannelHandler链的方式来实现的,先是实现一个Upstream的OPEN事件,再变换为Downstream的BOUND事件,不知为什么原因又触发了一个第七节的Upstream的BOUND事件出来,但是该事件并没有完成什么有效的操作。这个要存疑,待后续再阅读与品味!
分享到:
评论

相关推荐

    netty源码深入分析

    Bootstrap是启动Netty应用的基础类,通过它我们可以设置各种参数,如连接地址、线程组、通道类型等。一个典型的Bootstrap配置示例如下: ```java EventLoopGroup bossGroup = new NioEventLoopGroup(); ...

    netty源码分析之服务端启动全解析

    Netty是一款高性能的网络应用程序框架,它使用Java编程语言开发,主要用于网络应用程序的快速和易于开发,支持TCP和UDP...通过对Netty源码的深入分析,可以更好地理解其工作机制,对开发高性能的网络应用有极大的帮助。

    Netty权威指南-Netty源码

    源码分析时,首先需要关注的是 Netty 的启动流程,这通常从 `ServerBootstrap` 类开始。ServerBootstrap 配置了 EventLoopGroup(包含多个 EventLoop)和 Channel 实例,如 NioServerSocketChannel。然后通过绑定...

    06.NettyServerBootstrap启动的详细过程类图分析.rar

    在提供的资源 "课时07:NettyServerBootstrap启动的详细过程类图分析.avi" 中,可能包含了动画或讲解视频,详细展示了这些步骤的可视化过程,帮助理解NettyServerBootstrap启动时涉及的类和对象之间的关系,以及它们...

    Netty5.0架构剖析和源码解读.pdf

    本文档主要讲述了Netty5.0架构剖析和源码解读,涵盖了Netty的架构、源码分析、NIO入门等方面的知识点。 概述 JAVA 的 IO 演进是一个长期的过程,从传统的 BIO 通信到 NIO 的出现,都是为了解决通信中的问题。传统...

    高清_书签_Netty进阶之路 跟着案例学Netty.zip

    本书中的案例涵盖了Netty的启动和停止、内存、并发多线程、性能、可靠性、安全等方面,囊括了Netty绝大多数常用的功能及容易让人犯错的地方。在案例的分析过程中,还穿插讲解了Netty的问题定位思路、方法、技巧,...

    Netty3.x 源码解析

    在本系列文章中,作者也会结合代码分析来阐述Netty的各个组成部分,以便于读者能够更清晰地了解Netty的运作机制。作者欢迎任何形式的反馈,并且会根据读者的建议对文章内容进行更新和完善。同时,读者可以通过GitHub...

    netty 实现长连接

    9. **实例代码**:通常会包含如何配置和启动一个支持长连接的Netty服务器,以及客户端如何连接和发送数据的示例代码。 由于没有具体的博客内容,以上是基于常见情况的分析。实际的博客可能会详细解释这些概念,并...

    Java Netty异常分析BindException.pdf

    ### Java Netty 异常分析:BindException 在开发基于Java Netty的应用程序时,可能会遇到`java.net.BindException: Address already in use: no further information`这类错误。本篇文章将详细解析这一异常的原因、...

    Netty进阶之路:跟着案例学Netty 完整版.pdf

    《Netty进阶之路:跟着案例学Netty》中的案例涵盖了Netty的启动和停止、内存、并发多线程、性能、可靠性、安全等方面,囊括了Netty绝大多数常用的功能及容易让人犯错的地方。在案例的分析过程中,还穿插讲解了Netty...

    Netty权威指南完整版高清pdf

    最后,书中还包含了一些高级主题,如Netty的性能调优技巧、安全特性(如SSL/TLS加密)以及实战案例分析,帮助读者在实际项目中更好地应用Netty。 总的来说,《Netty权威指南》是一本全面且深入的Netty学习资料,...

    Netty-入门Netty编码

    Netty 是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议...通过 TimeServer 和 TimeClient 示例,我们可以逐步掌握 Netty 的基本用法,并为进一步探索其高级特性和源码分析打下基础。

    Netty和protocolbuf的通讯例子

    通过分析和运行这个示例,你可以更深入地理解如何将Netty和protobuf整合在一起,实现高效的数据通信。 总之,Netty和protobuf的结合为开发者提供了一种强大的网络通信解决方案。通过protobuf,我们可以定义清晰的...

    spring netty 整合 源代码

    4. **启动Netty服务器**:在Spring的启动类或者配置类中,使用Spring注入的Netty服务器配置启动服务器,监听指定端口。 5. **客户端交互**:创建Netty客户端,同样利用Spring管理的bean进行通信,也可以使用Spring...

    netty-socketio api接口文档.7z

    3. **ServerBootstrap类**:这是Netty中启动服务器的主要入口点。在API接口文档中,你会看到如何配置ServerBootstrap以设置各种参数,如线程池、管道工厂等。 4. **Pipeline和Handler**:Netty使用Pipeline来处理...

    netty 通过端口调用关闭

    1. **Netty 服务器启动与绑定端口** 当我们创建一个 Netty 服务器时,首先需要定义一个 `ServerBootstrap` 对象。这个对象负责配置服务器的行为,包括选择使用的线程模型(NIO 或 Epoll),处理入站和出站事件的 `...

    netty源码解析视频.txt

    - **Bootstrap**:是Netty应用启动引导类,用于初始化并启动Netty服务。 - **Channel**:是所有I/O操作的基础,可以理解为连接的抽象表示。 - **EventLoop**:负责处理I/O事件,包括注册、读取、写入等操作。 - **...

    NettyChannel

    NettyChannel是一个基于Netty框架的测试程序,它...通过分析和运行`NettyServer`和相应的客户端程序,可以深入了解Channel在实际操作中的行为,包括连接建立、数据交换和关闭流程,有助于开发者更好地掌握Netty框架。

Global site tag (gtag.js) - Google Analytics