成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Netty4.x 源碼實(shí)戰(zhàn)系列(二):服務(wù)端bind流程詳解

laoLiueizo / 674人閱讀

摘要:對于,目前大家只知道是個(gè)線程組,其內(nèi)部到底如何實(shí)現(xiàn)的,它的作用到底是什么,大家也都不太清楚,由于篇幅原因,這里不作詳細(xì)介紹,后面會有文章作專門詳解。

在上一篇《ServerBootstrap 與 Bootstrap 初探》中,我們已經(jīng)初步的了解了ServerBootstrap是netty進(jìn)行服務(wù)端開發(fā)的引導(dǎo)類。 且在上一篇的服務(wù)端示例中,我們也看到了,在使用netty進(jìn)行網(wǎng)絡(luò)編程時(shí),我們是通過bind方法的調(diào)用來完成服務(wù)器端口的偵聽:

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    ServerBootstrap b = new ServerBootstrap(); 
    b.group(bossGroup, workerGroup)  
     .channel(NioServerSocketChannel.class) 
     .handler(new LoggingHandler())   
     .childHandler(new ChannelInitializer() { 
         @Override
         public void initChannel(SocketChannel ch) throws Exception {
             ch.pipeline().addLast(new DiscardServerHandler());
         }
     })
     .option(ChannelOption.SO_BACKLOG, 128)        
     .childOption(ChannelOption.SO_KEEPALIVE, true);
    
     // 偵聽8000端口
     ChannelFuture f = b.bind(8000).sync(); 
    
     f.channel().closeFuture().sync();
} finally {
    workerGroup.shutdownGracefully();
    bossGroup.shutdownGracefully();
}

從上面的服務(wù)端示例中可以看到,我們只是定義了主線程組及worker線程組,以及指定了channel類型為NioServerSocketChannel等等一些簡單的配置, 然后綁定偵聽端口,用于網(wǎng)絡(luò)服務(wù)的主體代碼基本就完了(業(yè)務(wù)代碼在Handler中實(shí)現(xiàn),后面的文章會詳細(xì)介紹。
這真的大大簡化并方便了java程序員使用netty來進(jìn)行網(wǎng)絡(luò)開發(fā),但是想要深入學(xué)習(xí)netty的人可能會有下面的一些疑問:

netty是繼續(xù)Java NIO的,那么ServerSocketChannel是什么時(shí)候初始化的?

我怎么沒有看到Java NIO中的selector, netty是如何實(shí)現(xiàn)多路復(fù)用的?

我們設(shè)置的handler 或者 childHandler,是如何應(yīng)用的?

boss線程組 以及 worker線程組 其實(shí)如何分配線程的?

為什么是boss線程組,難道接收用戶請求是多個(gè)線程一起工作?

。。。

本篇將帶著上面這些疑問,我們將進(jìn)入bind方法內(nèi)部,深入淺出,對netty的工作機(jī)制一探究竟。

當(dāng)我們調(diào)用ServerBootstrap的bind方法時(shí),其實(shí)是調(diào)用的是父類AbstractBootstrap的bind方法:

public ChannelFuture bind(int inetPort) {
    return bind(new InetSocketAddress(inetPort));
}

進(jìn)而調(diào)用另一個(gè)重載bind方法:

public ChannelFuture bind(SocketAddress localAddress) {
    validate();
    if (localAddress == null) {
        throw new NullPointerException("localAddress");
    }
    return doBind(localAddress);
}

此bind(SocketAddress localAddress)內(nèi)部有兩個(gè)調(diào)用
1、 調(diào)用validate()
顧名思義,validate應(yīng)該是做校驗(yàn),由于ServerBootstrap覆蓋了AbstractBootstrap方法,因此此validate其實(shí)是調(diào)用ServerBootstrap中的validate方法:

@Override
public ServerBootstrap validate() {
    super.validate();
    if (childHandler == null) {
        throw new IllegalStateException("childHandler not set");
    }
    if (childGroup == null) {
        logger.warn("childGroup is not set. Using parentGroup instead.");
        childGroup = config.group();
    }
    return this;
}

在子類ServerBootstrap的validate方法中,首先它有調(diào)用了基類的validate()方法:

public B validate() {
    if (group == null) {
        throw new IllegalStateException("group not set");
    }
    if (channelFactory == null) {
        throw new IllegalStateException("channel or channelFactory not set");
    }
    return self();
}

所以通過validate方法我們得出如下結(jié)論:

1) 服務(wù)端程序必須要設(shè)置boss線程組 以及 worker線程組,分別用于Acceptor 以及 I/O操作; 
2)必須通過Boostrap的channel()來指定通道類型,用來生成相應(yīng)的通道(ServerSocketChannel或SocketChannel);
3) 因?yàn)槭欠?wù)端程序,所以必須設(shè)置ChildHandler,來指定業(yè)務(wù)處理器,否則沒有業(yè)務(wù)處理的服務(wù)器hi沒有意義的;

2、 調(diào)用doBind(localAddress)
首先我們先看一下AbstractBootstrap中doBind方法代碼片段:

private ChannelFuture doBind(final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();      // (1)
    final Channel channel = regFuture.channel();            // (2)
    if (regFuture.cause() != null) {
        return regFuture;
    }

    if (regFuture.isDone()) {
        ChannelPromise promise = channel.newPromise();
        doBind0(regFuture, channel, localAddress, promise);     // (3)
        return promise;
    } else {
        final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
        regFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                Throwable cause = future.cause();
                if (cause != null) {
                    promise.setFailure(cause);
                } else {
                    promise.registered();

                    doBind0(regFuture, channel, localAddress, promise);  // (3)
                }
            }
        });
        return promise;
    }
}

剝?nèi)o用代碼,其實(shí)doBind方法內(nèi)部,只做了兩件事:
一、調(diào)用了initAndRegister方法
二、調(diào)用用了doBind0方法
到底這兩個(gè)方法做了啥工作,我們繼續(xù)往下分析。

一、首先看看 initAndRegister方法內(nèi)部代碼:

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        // (1) 調(diào)用工廠方法,生成channel實(shí)例
        channel = channelFactory.newChannel();
        
        // (2) 初始化通道信息
        init(channel);
    } catch (Throwable t) {
        if (channel != null) {
            channel.unsafe().closeForcibly();
        }
        
        return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
    }

    // (3) 注冊通道
    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }

    return regFuture;
}

通過上面代碼,我們可以看出initAndRegister方法做了三件事:

①、調(diào)用channelFactory生成通道channel實(shí)例:

在上一篇中,我們已經(jīng)知道,通過serverbootstrap的channel方法來指定通道類型,其實(shí)是調(diào)用基類AbstractBoostrap的channel方法,其內(nèi)部其實(shí)是實(shí)例化了一個(gè)產(chǎn)生指定channel類型的channelFactory。

所以,initAndRegister中的channelFactory.newChannel()方法就是生成了一個(gè)NioServerSocketChannel的實(shí)例。 關(guān)于NioServerSocketChannel內(nèi)部細(xì)節(jié),我會有專門的文章進(jìn)行分析,此處不做詳述。

②、調(diào)用init(channel)初始化通道信息

init方法在基類AbstractBootstrap中是一個(gè)抽象方法:

abstract void init(Channel channel) throws Exception;

所以此處init的具體實(shí)現(xiàn)在子類ServerBootstrap類中:

@Override
void init(Channel channel) throws Exception {
    // 設(shè)置引導(dǎo)類配置的option
    final Map, Object> options = options0();
    synchronized (options) {
        setChannelOptions(channel, options, logger);
    }
    // 設(shè)置引導(dǎo)類配置的attr
    final Map, Object> attrs = attrs0();
    synchronized (attrs) {
        for (Entry, Object> e: attrs.entrySet()) {
            @SuppressWarnings("unchecked")
            AttributeKey key = (AttributeKey) e.getKey();
            channel.attr(key).set(e.getValue());
        }
    }
    
    // 獲取當(dāng)前通道的pipeline
    ChannelPipeline p = channel.pipeline();

    final EventLoopGroup currentChildGroup = childGroup;
    final ChannelHandler currentChildHandler = childHandler;
    final Entry, Object>[] currentChildOptions;
    final Entry, Object>[] currentChildAttrs;
    synchronized (childOptions) {
        currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
    }
    synchronized (childAttrs) {
        currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
    }

    // 給NioServerSocketChannel的pipeline中添加一個(gè)ChannelInitializer類型的Handler
    p.addLast(new ChannelInitializer() {
        @Override
        public void initChannel(final Channel ch) throws Exception {
            final ChannelPipeline pipeline = ch.pipeline();
            ChannelHandler handler = config.handler();
            if (handler != null) {
                pipeline.addLast(handler);
            }

            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    pipeline.addLast(new ServerBootstrapAcceptor(
                            ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}

init內(nèi)部主要做了一下幾件事:
ⅰ、 設(shè)置channel的options

final Map, Object> options = options0();
synchronized (options) {
    setChannelOptions(channel, options, logger);
}

ⅱ、設(shè)置channel的attribute

final Map, Object> attrs = attrs0();
synchronized (attrs) {
    for (Entry, Object> e: attrs.entrySet()) {
        @SuppressWarnings("unchecked")
        AttributeKey key = (AttributeKey) e.getKey();
        channel.attr(key).set(e.getValue());
    }
}

ⅲ、給NioServerSocketChannel的pipeline中添加一個(gè)ChannelInitializer類型的Handler(根據(jù)類繼承ChannelInitializer繼承自ChannelInboundHandlerAdapter)

關(guān)于pipeline到底是什么,本篇不做詳述,下一篇我會跟NioServerSocketChannel來一起給大家分析一下。

③、完成通道的注冊
通道初始化完成后,然后就可以注冊通道了:

 ChannelFuture regFuture = config().group().register(channel);

config()在AbstractBootstrap中也是個(gè)抽象方法:

public abstract AbstractBootstrapConfig config();

所以具體的實(shí)現(xiàn)細(xì)節(jié)還是在子類ServerBootstrap中:

@Override
public final ServerBootstrapConfig config() {
    return config;
}

此方法只會返回了config實(shí)例對象,此屬性是在ServerBootstrap初始化時(shí)就創(chuàng)建了

public class ServerBootstrap extends AbstractBootstrap {
    ...
    private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
    ...
    
    @Override
    public final ServerBootstrapConfig config() {
        return config;
    }
}

我們先看一下ServerBootstrapConfig的類繼承結(jié)構(gòu)圖:

ServerBootstrapConfig初始化時(shí)傳入的this對象,此this表示ServerBootstrap,而且ServerBootstrapConfig構(gòu)造方法內(nèi)部調(diào)用了其基類AbstractBootstrapConfig的構(gòu)造方法:

ServerBootstrapConfig(ServerBootstrap bootstrap) {
    super(bootstrap);
}

所以config().group()就對應(yīng)ServerBootstrap的group屬性引用(由上一篇得知group指向boss線程組),因此register其實(shí)是調(diào)用的NioEventLoopGroup的register方法。

對于NioEventLoopGroup,目前大家只知道是個(gè)線程組,其內(nèi)部到底如何實(shí)現(xiàn)的,它的作用到底是什么,大家也都不太清楚,由于篇幅原因,這里不作詳細(xì)介紹,后面會有文章作專門詳解。

二、我們再回到doBind(localAddress)方法,內(nèi)部在調(diào)用玩initAndRegister之后,就是調(diào)用doBind0方法

 private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {

    // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
    // the pipeline in its channelRegistered() implementation.
    channel.eventLoop().execute(new Runnable() {
        @Override
        public void run() {
            if (regFuture.isSuccess()) {
                channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            } else {
                promise.setFailure(regFuture.cause());
            }
        }
    });
}

此方法內(nèi)部就是完成channel的偵聽端口的綁定。

至此ServerBootstrap的bind工作執(zhí)行完成。

此篇對服務(wù)端綁定的流程做了大體介紹,但由于篇幅問題,下面幾個(gè)問題未做詳盡分析:
1、 NioServerSocketChannel是如何實(shí)例化的
2、 Pipeline是什么,為什么要通過它添加handler
3、 NioEventLoopGroup內(nèi)部細(xì)節(jié)是什么,為什么要通過它注冊Channel, Java NIO中channel初始化后不是要注冊到selector中嗎?

帶著上面這些疑問,歡迎大家繼續(xù)關(guān)注接下來的幾篇文章,在這幾篇文章中,bind操作會一直貫穿其中:
Netty4.x 源碼實(shí)戰(zhàn)系列(三):NioServerSocketChannel全剖析
Netty4.x 源碼實(shí)戰(zhàn)系列(四):Pipeline全剖析
Netty4.x 源碼實(shí)戰(zhàn)系列(五):NioEventLoopGroup全剖析
Netty4.x 源碼實(shí)戰(zhàn)系列(六):NioEventLoop全剖析

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/68105.html

相關(guān)文章

  • Netty4.x 源碼實(shí)戰(zhàn)系列(三):NioServerSocketChannel全剖析

    摘要:本篇將通過實(shí)例化過程,來深入剖析。及初始化完成后,它們會相互連接。我們在回到的構(gòu)造方法父類構(gòu)造方法調(diào)用完成后,還要初始化一下自己的配置對象是的內(nèi)部類而又是繼承自,通過代碼分析,此對象就是就會對底層一些配置設(shè)置行為的封裝。 根據(jù)上一篇《Netty4.x 源碼實(shí)戰(zhàn)系列(二):服務(wù)端bind流程詳解》所述,在進(jìn)行服務(wù)端開發(fā)時(shí),必須通過ServerBootstrap引導(dǎo)類的channel方法來...

    Flink_China 評論0 收藏0
  • Netty4.x 源碼實(shí)戰(zhàn)系列(四):Pipeline全剖析

    摘要:在上一篇源碼實(shí)戰(zhàn)系列三全剖析中,我們詳細(xì)分析了的初始化過程,并得出了如下結(jié)論在中,每一個(gè)都有一個(gè)對象,并且其內(nèi)部本質(zhì)上就是一個(gè)雙向鏈表本篇我們將深入源碼內(nèi)部,對其一探究竟,給大家一個(gè)全方位解析。 在上一篇《Netty4.x 源碼實(shí)戰(zhàn)系列(三):NioServerSocketChannel全剖析》中,我們詳細(xì)分析了NioServerSocketChannel的初始化過程,并得出了如下結(jié)論...

    13651657101 評論0 收藏0
  • Netty4.x 源碼實(shí)戰(zhàn)系列(一):ServerBootstrap 與 Bootstrap 初探

    摘要:而用于主線程池的屬性都定義在中本篇只是簡單介紹了一下引導(dǎo)類的配置屬性,下一篇我將詳細(xì)介紹服務(wù)端引導(dǎo)類的過程分析。 從Java1.4開始, Java引入了non-blocking IO,簡稱NIO。NIO與傳統(tǒng)socket最大的不同就是引入了Channel和多路復(fù)用selector的概念。傳統(tǒng)的socket是基于stream的,它是單向的,有InputStream表示read和Outpu...

    BakerJ 評論0 收藏0
  • 【自己讀源碼Netty4.X系列(三) Channel Register

    摘要:我想這很好的解釋了中,僅僅一個(gè)都這么復(fù)雜,在單線程或者說串行的程序中,編程往往是很簡單的,說白了就是調(diào)用,調(diào)用,調(diào)用然后返回。 Netty源碼分析(三) 前提概要 這次停更很久了,原因是中途迷茫了一段時(shí)間,不過最近調(diào)整過來了。不過有點(diǎn)要說下,前幾天和業(yè)內(nèi)某個(gè)大佬聊天,收獲很多,所以這篇博文和之前也會不太一樣,我們會先從如果是我自己去實(shí)現(xiàn)這個(gè)功能需要怎么做開始,然后去看netty源碼,與...

    darkbug 評論0 收藏0
  • Netty4.x 源碼實(shí)戰(zhàn)系列(五):深入淺出學(xué)NioEventLoopGroup

    摘要:接下來的兩篇文章,我將從源碼角度為大家深入淺出的剖析的線程模型工作機(jī)制。我們看一下的源碼通過的代碼發(fā)現(xiàn),實(shí)現(xiàn)了接口,其內(nèi)部會通過指定的默認(rèn)線程工廠來創(chuàng)建線程,并執(zhí)行相應(yīng)的任務(wù)。至此,初始化完成了。下一篇我們將詳細(xì)介紹,敬請期待。 我們都知道Netty的線程模型是基于React的線程模型,并且我們都知道Netty是一個(gè)高性能的NIO框架,那么其線程模型必定是它的重要貢獻(xiàn)之一。 在使用ne...

    MSchumi 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<