欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Netty啟動流程注冊多路復用源碼解析

 更新時間:2022年03月25日 10:35:33   作者:向南是個萬人迷  
這篇文章主要介紹了Netty啟動流程注冊多路復用源碼分析,繼續(xù)分析channel是如何注冊到selector中的,有需要的朋友可以借鑒參考下,希望能夠有所幫助

前文傳送門:Netty啟動流程服務端channel初始化

注冊多路復用

回到上一小節(jié)的代碼:

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        //創(chuàng)建channel
        channel = channelFactory.newChannel();
        //初始化channel
        init(channel);
    } catch (Throwable t) {
        //忽略非關鍵代碼
    }
    //注冊channel
    ChannelFuture regFuture = config().group().register(channel);
    //忽略非關鍵代碼
    return regFuture;
}

注冊channel的步驟

我們講完創(chuàng)建channel和初始化channel的關鍵步驟, 我們繼續(xù)跟注冊channel的步驟:

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

其中, 重點關注下register(channel)這個方法, 這個方法最終會調(diào)用到AbstractChannel中內(nèi)部類AbstractUnsafe的register()方法, 具體如何調(diào)用到這個方法, 可以簡單帶大家捋一下

首先看下config()方法

由于是ServerBootstrap調(diào)用的, 所以我們跟進去:

public final ServerBootstrapConfig config() {
    return config;
}

返回的config是ServerBootrap的成員變量config:

private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);

跟到ServerBootstrapConfig的構(gòu)造方法:

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

繼續(xù)跟到其父類AbstractBootstrapConfig的構(gòu)造方法:

protected AbstractBootstrapConfig(B bootstrap) {
    this.bootstrap = ObjectUtil.checkNotNull(bootstrap, "bootstrap");
}

我們發(fā)現(xiàn)我們創(chuàng)建的ServerBootstrap作為參數(shù)初始化了其成員變量bootstrap

回到initAndRegister()方法:

config()返回的是ServerBootstrapConfig對象

再繼續(xù)跟到其group()方法:

public final EventLoopGroup group() {
    return bootstrap.group();
}

這里調(diào)用Bootstrap的group()方法:

public final EventLoopGroup group() {
    return group;
}

這里返回了AbstractBootstrap的成員變量group, 我們回顧下第一小節(jié), 還記得AbstractBootstrap的group(EventLoopGroup group)方法嗎?

public B group(EventLoopGroup group) {
    this.group = group;
    return (B) this;
}

group(EventLoopGroup group)方法初始化了我們boss線程, 而group()返回了boss線程, 也就是說 config().group().register(channel) 中的register()方法是boss線程對象調(diào)用的, 由于我們當初初始化的是NioEventLoopGroup, 因此走的是NioEventLoopGroup的父類的MultithreadEventLoopGroup的register()方法

跟到MultithreadEventLoopGroup的register()方法:

public ChannelFuture register(Channel channel) {
    return next().register(channel);
}

這里的代碼看起來有點暈, 沒關系, 以后會講到, 現(xiàn)在可以大概做個了解, NioEventLoopGroup是個線程組, 而next()方法就是從線程組中選出一個線程, 也就是NioEventLoop線程, 所以這里的next()方法返回的是NioEventLoop對象, 其中register(channel)最終會調(diào)用NioEventLoop的父類SingleThreadEventLoop的register(channel)方法

跟到SingleThreadEventLoop的register(channel)方法:

public ChannelFuture register(Channel channel) {
    return register(new DefaultChannelPromise(channel, this));
}

其中DefaultChannelPromise類我們之后也會講到

我們先跟到register(new DefaultChannelPromise(channel, this)):

public ChannelFuture register(final ChannelPromise promise) {
    ObjectUtil.checkNotNull(promise, "promise");
    promise.channel().unsafe().register(this, promise);
    return promise;
}

channel()會返回我們初始化的NioServerSocketChannel, unsafe()會返回我們創(chuàng)建channel的時候初始化的unsafe對象

跟進去看AbstractChannel的unsafe()的實現(xiàn):

public Unsafe unsafe() {
    return unsafe;
}

這里返回的unsafe, 就是我們初始化channel創(chuàng)建的unsafe

回顧下第二小節(jié)channel初始化的步驟:

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

我們看unsafe的初始化:unsafe=newUnsafe()

跟到newUnsafe()中, 我們之前講過NioServerSokectChannel的父類是AbstractNioMessageChannel, 所以會調(diào)用到到AbstractNioMessageChannel類中的newUnsafe()

跟到AbstractNioMessageChannel類中的newUnsafe():

protected AbstractNioUnsafe newUnsafe() {
    return new NioMessageUnsafe();
}

我們看到這里創(chuàng)建了NioMessageUnsafe()對象, 所以在 promise.channel().unsafe().register(this, promise) 代碼中, unsafe()是返回的NioMessageUnsafe()對象, 最后調(diào)用其父類AbstractUnsafe(也就是AbstractChannel的內(nèi)部類)的register()方法,

簡單介紹下unsafe接口, unsafe顧名思義就是不安全的, 因為很多對channel的io方法都定義在unsafe中, 所以netty將其作為內(nèi)部類進行封裝, 防止被外部直接調(diào)用, unsafe接口是Channel接口的內(nèi)部接口, unsafe的子類也分別封裝在Channel的子類中, 比如我們現(xiàn)在剖析的register()方法, 就是封裝在AbstractChannel類的內(nèi)部類AbstractUnsafe中的方法, 有關Unsafe和Channel的繼承關系如下:

以上內(nèi)容如果不明白沒有關系, 有關NioEventLoop相關會在后面的章節(jié)講到, 目前我們只是了解是如何走到AbstractUnsafe類的register()即可

我們繼續(xù)看看register()方法:

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    //代碼省略
    //所有的復制操作, 都交給eventLoop處理(1)
    AbstractChannel.this.eventLoop = eventLoop;
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    //做實際主注冊(2)
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            //代碼省略
        }
    }
}

我們跟著注釋的步驟繼續(xù)走, 第一步, 綁定eventLoop線程:

AbstractChannel.this.eventLoop = eventLoop;

eventLoop是AbstractChannel的成員變量, 有關eventLoop, 我們會在緒章節(jié)講到, 這里我們只需要知道, 每個channel綁定唯一的eventLoop線程, eventLoop線程和channel的綁定關系就是在這里展現(xiàn)的

再看第二步, 做實際注冊:

我們先看if判斷, if(eventLoop.inEventLoop())

這里是判斷是不是eventLoop線程, 顯然我們現(xiàn)在是main()方法所在的線程, 所以走的else, eventLoop.execute()是開啟一個eventLoop線程, 而register0(promise)就是再開啟線程之后, 通過eventLoop線程執(zhí)行的, 這里大家暫時作為了解

我們重點關注register0(promise), 跟進去:

private void register0(ChannelPromise promise) {
    try {
        //做實際的注冊(1)
        doRegister();
        neverRegistered = false;
        registered = true;
        //觸發(fā)事件(2)
        pipeline.invokeHandlerAddedIfNeeded();
        safeSetSuccess(promise);
        //觸發(fā)注冊成功事件(3)
        pipeline.fireChannelRegistered();
        if (isActive()) {
            if (firstRegistration) {
                //傳播active事件(4)
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {
                beginRead();
            }
        }
    } catch (Throwable t) {
        //省略代碼
    }
}

我們重點關注doRegister()這個方法

doRegister()最終會調(diào)用AbstractNioChannel的doRegister()方法:

protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            //jdk底層的注冊方法
            //第一個參數(shù)為selector, 第二個參數(shù)表示不關心任何事件
            selectionKey = javaChannel().register(eventLoop().selector, 0, this);
            return;
        } catch (CancelledKeyException e) {
            //省略代碼
        }
    }
}

我們終于看到和java底層相關的方法了

跟到javaChannel()的方法中:

protected SelectableChannel javaChannel() {
    return ch;
}

這個ch, 就是本章第二小節(jié)創(chuàng)建NioServerSocketChannel中初始化的jdk底層ServerSocketChannel

這里register(eventLoop().selector, 0, this)方法中eventLoop().selector, 是獲得每一個eventLoop綁定的唯一的selector, 0代表這次只是注冊, 并不監(jiān)聽任何事件, this是代表將自身(NioEventLoopChannel)作為屬性綁定在返回的selectionKey當中, 這個selectionKey就是與每個channel綁定的jdk底層的SelectionKey對象, 熟悉nio的小伙伴應該不會陌生, 這里不再贅述

回到register0(ChannelPromise promise)方法, 我們看后續(xù)步驟:

步驟(2)是觸發(fā)handler的需要添加事件, 事件傳遞的內(nèi)容我們將在后續(xù)課程詳細介紹, 這里不必深究

步驟(3)是觸發(fā)注冊成功事件(3), 同上

步驟(4)是傳播active事件(4), 這里簡單強調(diào)一下, 這里的方法pipeline.fireChannelActive()第一個注冊是執(zhí)行不到的, 因為isActive()會返回false, 因為鏈路沒完成

本小節(jié)梳理了有注冊多路復用的相關邏輯, 同學們可以跟著代碼自己走一遍以加深印象

以上就是Netty啟動流程注冊多路復用源碼分析的詳細內(nèi)容,更多關于Netty啟動流程的資料請關注腳本之家其它相關文章!

相關文章

  • spring boot實現(xiàn)軟刪除的示例代碼

    spring boot實現(xiàn)軟刪除的示例代碼

    這篇文章主要介紹了spring boot實現(xiàn)軟刪除的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • Java?Bean轉(zhuǎn)Map的那些踩坑實戰(zhàn)

    Java?Bean轉(zhuǎn)Map的那些踩坑實戰(zhàn)

    項目中有時會遇到Map轉(zhuǎn)Bean,Bean轉(zhuǎn)Map的情況,下面這篇文章主要給大家介紹了關于Java?Bean轉(zhuǎn)Map那些踩坑的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-07-07
  • 一篇超詳細的Spring Boot對jdbc支持的文章

    一篇超詳細的Spring Boot對jdbc支持的文章

    JdbcTemplate 是在JDBC API基礎上提供了更抽象的封裝,并提供了基于方法注解的事務管理能力。 通過使用SpringBoot自動配置功能并代替我們自動配置beans,下面給大家介紹spring boot中使用JdbcTemplate相關知識,一起看看吧
    2021-07-07
  • Java字符串詳解的實例介紹

    Java字符串詳解的實例介紹

    本篇文章介紹了,在Java中關于字符串詳解一些實例操作,需要的朋友參考下
    2013-04-04
  • Java Web端程序?qū)崿F(xiàn)文件下載的方法分享

    Java Web端程序?qū)崿F(xiàn)文件下載的方法分享

    這篇文章主要介紹了Java Web端程序?qū)崿F(xiàn)文件下載的方法分享,包括一個包含防盜鏈功能的專門針對圖片下載的程序代碼示例,需要的朋友可以參考下
    2016-05-05
  • 初次體驗MyBatis的注意事項

    初次體驗MyBatis的注意事項

    今天給大家?guī)淼氖顷P于MyBatis的相關知識,文章圍繞著MyBatis的用法展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Android bdflow數(shù)據(jù)庫神器的使用

    Android bdflow數(shù)據(jù)庫神器的使用

    這篇文章主要介紹了Android bdflow數(shù)據(jù)庫神器的使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • 使用stream的Collectors.toMap()方法常見的問題及解決

    使用stream的Collectors.toMap()方法常見的問題及解決

    這篇文章主要介紹了使用stream的Collectors.toMap()方法常見的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 如何在mybatis中向BLOB字段批量插入數(shù)據(jù)

    如何在mybatis中向BLOB字段批量插入數(shù)據(jù)

    這篇文章主要介紹了如何在mybatis中向BLOB字段批量插入數(shù)據(jù)的相關知識,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2020-10-10
  • Spring Boot利用@Async異步調(diào)用:ThreadPoolTaskScheduler線程池的優(yōu)雅關閉詳解

    Spring Boot利用@Async異步調(diào)用:ThreadPoolTaskScheduler線程池的優(yōu)雅關閉詳解

    這篇文章主要給大家介紹了關于Spring Boot利用@Async異步調(diào)用:ThreadPoolTaskScheduler線程池的優(yōu)雅關閉的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧
    2018-05-05

最新評論