`

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事件出来,但是该事件并没有完成什么有效的操作。这个要存疑,待后续再阅读与品味!
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics