关闭

Netty 学习 - Server与Client的启动过程

756人阅读 评论(0) 收藏 举报

    本文主要记录在学习Netty4的代码中学习到的知识点,方便后续的查看


第一部分: 服务端ServerBootstrap与客户端BootStrap


ServerBootStrap与BootStrap均继承于AbstactBootStrap,AbstractBootstrap完成了大部分的模板方法,提供抽象方法供服务端和客户端启动类实现


启动流程如下:服务端BIND


 InitAndRegister  new - channel(包括内部的unsafe,pipleline, readInterestOp(后续再active触发的read中修改selectKey))


channle对象建立后会执行init方法,其中init方法是抽象方法,ServerBootStrap和BootStrap会实现对应的init方法


服务端的channel是NioServerSocketChannel,客户端的channel是NioSocketChannel


init方法包括设置TCP的OPTION参数等,最关键的是服务端和客户端会在init中对pipeline增加不同的ChannelHandler


ServerBootStrap的init方法会将ServerBootstrapAcceptor添加到pipleline中(将连接handler添加到pipleline,将用户配置的childHandler封装到ServerBootstrapAcceptor中)
(ServerBootstrapAcceptor的实际添加过程会封装在ChannelInitializer的一个匿名类中,)


BootStrap的init方法会将用户配置的handler添加的pipleline中


以上步骤完成了InitAndRegister中的init方法,接着看Register方法


统一实现代码为 ChannelFuture regFuture = config().group().register(channel);


含义为从EventLoopGroup中循环选择一个eventLoop用户注册这个channel,代码为promise.channel().unsafe().register(this, promise);
最后是channle的unsafe对象的register方法,此时会将channle与eventloop进行绑定,最后执行到AbstractNioChannel的doRegister方法,该方法会将对应的nioSocketChannel注册到eventLoop上的多路复用器selector,每个eventLoop都对应一个selector,此时注册的事件是0


继续pipeline.fireChannelRegistered(); 


对于服务端,此时的pipeline的handler如下 :  head - ChannelInitializer- tail , 在执行到ChannelInitializer时,会将ServerBootstrapAcceptor添加到pipleline中


对于客户端,此时的pipleline的handler如下: head- 用户handler- tail,执行到用户handler时,会将用户配置添加到pipeline中,一般来说,用户handler也是一个ChannelInitializer的匿名类


ChannelInitializer的channelRegistered方法再调用完initChannel后,会将本身从pipeline中移除,并且继续fireChannelRegistered


完成以上步骤后,对于服务端需要执行bind操作,对于客户端需要执行connect操作


对于服务端,bind属于outbound事件,由pipeline.bind完成,因为是outbound事件,所以从tail开始,到head的bind,执行到unsafe的bind
unsafe主要有两个关键的动作,第一doBind,第二pipleline.fireChannleActive.


doBind 调用java的NioSocketChannel完成bind,fireChannleActive很关键,属于Inbound事件,从head开始,在headHandler中的方法如下:
 
  @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelActive();


            readIfIsAutoRead();
        }

关键则是readifIsAutoRead方法,触发channel.read() - pipeline.read - tail.read -  head.read - unsafe.beginRead  - abstractChannel.doBeginRead
对于NIO框架,则是AbstractNioChannel.doBeginRead
 
    @Override
    protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }


        readPending = true;


        final int interestOps = selectionKey.interestOps();
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }


实际上就是将channel的interestOps修改为合适的值,比如说对于Server端,需要修改为Accept,对于Client端,则需要修改为Read,上述代码中的readInterestOp实际上就是本文开始说的channle初始化的时候设置好的,
    /**
     * Create a new instance using the given {@link ServerSocketChannel}.
     */
    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }




     /**
     * Create a new instance
     *
     * @param parent            the parent {@link Channel} by which this instance was created. May be {@code null}
     * @param ch                the underlying {@link SelectableChannel} on which it operates
     */
    protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
        super(parent, ch, SelectionKey.OP_READ);
    }


客户端的NioSocketChannel继承AbstractNioByteChannel


对于客户端,执行的则是connect方法,同样写出执行步骤


BootStrap.doConnect - channel.connect - pipeline.connect  - tail.connect - head.connect -unsafe.connect - AbstractNioUnsafe.connect


在connect后,由于是异步操作,将selectKey设置为connect,当connect完成后,由客户端channel对应的eventLoop轮询selector后调用unsafe.finishConnect


方法内部会有 fulfillConnectPromise,该方法在connect完成后执行pipeline().fireChannelActive();完成前面所讲的将客户端的selectKey设置为Read的关键步骤


综上所述,当服务端执行了bind后,修改selectKey为accept后,则完成了启动过程。客户端执行connect后,修改selectKey为read后,则完成了连接过程


这两个修改selectKey都发送在fireChannelActive后,对于服务端则是在bind后,对于客户端则是在connect后







   
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:38764次
    • 积分:553
    • 等级:
    • 排名:千里之外
    • 原创:22篇
    • 转载:1篇
    • 译文:0篇
    • 评论:1条
    文章分类