国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Netty4.x 源碼實戰系列(五):深入淺出學NioEventLoopGroup

MSchumi / 2738人閱讀

摘要:接下來的兩篇文章,我將從源碼角度為大家深入淺出的剖析的線程模型工作機制。我們看一下的源碼通過的代碼發現,實現了接口,其內部會通過指定的默認線程工廠來創建線程,并執行相應的任務。至此,初始化完成了。下一篇我們將詳細介紹,敬請期待。

我們都知道Netty的線程模型是基于React的線程模型,并且我們都知道Netty是一個高性能的NIO框架,那么其線程模型必定是它的重要貢獻之一。

在使用netty的服務端引導類ServerBootstrap或客戶端引導類Bootstrap進行開發時,都需要通過group屬性指定EventLoopGroup, 因為是開發NIO程序,所以我們選擇NioEventLoopGroup。

接下來的兩篇文章,我將從源碼角度為大家深入淺出的剖析Netty的React線程模型工作機制。

本篇側重點是NioEventLoopGroup。

首先我們先回顧一下,服務端初始化程序代碼(省略非相關代碼):

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    ServerBootstrap b = new ServerBootstrap(); 
    b.group(bossGroup, workerGroup);
    
    ... // 已省略非相關代碼
    
     // 偵聽8000端口
     ChannelFuture f = b.bind(8000).sync(); 
    
     f.channel().closeFuture().sync();
} finally {
    workerGroup.shutdownGracefully();
    bossGroup.shutdownGracefully();
}

在分析源碼之前,我們先看看NioEventLoopGroup的類繼承結構圖:

初始化bossGroup及workerGroup時,使用了NioEventLoopGroup的無參構造方法,本篇將從此無參構造入手,詳細分析NioEventLoopGroup的初始化過程。

首先我們看看NioEventLoopGroup的無參構造方法:

public NioEventLoopGroup() {
    this(0);
}

其內部繼續調用器構造方法,并指定線程數為0:

public NioEventLoopGroup(int nThreads) {
    this(nThreads, (Executor) null);
}

繼續調用另一個構造方法,指定線程為0,且Executor為null:

public NioEventLoopGroup(int nThreads, Executor executor) {
    this(nThreads, executor, SelectorProvider.provider());
}

在此構造中,它會指定selector的輔助類 "SelectorProvider.provider()",我們繼續查看它的調用:

public NioEventLoopGroup(
            int nThreads, Executor executor, final SelectorProvider selectorProvider) {
    this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}

此構造方法中,初始化了一個默認的選擇策略工廠,用于生成select策略:

public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
                             final SelectStrategyFactory selectStrategyFactory) {
    super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}

經過上面一系列的構造方法調用,此時個參數值對應如下:
nThreads:0
executor: null
selectorProvider: SelectorProvider.provider()
selectStrategyFactory: DefaultSelectStrategyFactory.INSTANCE
以及指定了拒絕策略RejectedExecutionHandlers.reject()

接著其會調用父類MultithreadEventLoopGroup的MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args)構造方法

protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
    super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}

此構造方法主要做了一件事,就是當指定的線程數為0時,使用默認的線程數DEFAULT_EVENT_LOOP_THREADS,此只是在MultithreadEventLoopGroup類被加載時完成初始化

private static final int DEFAULT_EVENT_LOOP_THREADS;

static {
    DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
            "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

    if (logger.isDebugEnabled()) {
        logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
    }
}

所以根據代碼,我們得出,如果初始化NioEventLoopGroup未指定線程數時,默認是CPU核心數*2

接著繼續調用父類MultithreadEventExecutorGroup的MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args)構造方法

protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
    this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}

在此構造方法中,我們指定了一個EventExecutor的選擇工廠DefaultEventExecutorChooserFactory,此工廠主要是用于選擇下一個可用的EventExecutor, 其內部有兩種選擇器, 一個是基于位運算,一個是基于普通的輪詢,它們的代碼分別如下:

基于位運算的選擇器PowerOfTwoEventExecutorChooser

private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
    private final AtomicInteger idx = new AtomicInteger();
    private final EventExecutor[] executors;

    PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
        this.executors = executors;
    }

    @Override
    public EventExecutor next() {
        return executors[idx.getAndIncrement() & executors.length - 1];
    }
}

基于普通輪詢的選擇器GenericEventExecutorChooser

private static final class GenericEventExecutorChooser implements EventExecutorChooser {
    private final AtomicInteger idx = new AtomicInteger();
    private final EventExecutor[] executors;

    GenericEventExecutorChooser(EventExecutor[] executors) {
        this.executors = executors;
    }

    @Override
    public EventExecutor next() {
        return executors[Math.abs(idx.getAndIncrement() % executors.length)];
    }
}

我們接著回到剛剛的構造器,其內部會繼續調用MultithreadEventExecutorGroup的另一個構造方法,此構造方法是NioEventLoopGroup的核心代碼

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
    
    // 為了便于代碼剖析,以省略非相關代碼
    
    // 初始化executor
    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }

    // 初始化EventExecutor
    children = new EventExecutor[nThreads];

    for (int i = 0; i < nThreads; i ++) {
       
            children[i] = newChild(executor, args);
         
    }

    // 生成選擇器對象
    chooser = chooserFactory.newChooser(children);
}

此構造方法主要做了三件事:
1、初始化executor為ThreadPerTaskExecutor的實例:
通過前面的構造方法調用,我們知道executor為null,所以在此構造方法中,executor會被初始化為ThreadPerTaskExecutor實例。我們看一下ThreadPerTaskExecutor的源碼:

public final class ThreadPerTaskExecutor implements Executor {
    private final ThreadFactory threadFactory;

    public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
        if (threadFactory == null) {
            throw new NullPointerException("threadFactory");
        }
        this.threadFactory = threadFactory;
    }

    @Override
    public void execute(Runnable command) {
        threadFactory.newThread(command).start();
    }
}

通過ThreadPerTaskExecutor 的代碼發現,ThreadPerTaskExecutor 實現了Executor接口,其內部會通過newDefaultThreadFactory()指定的默認線程工廠來創建線程,并執行相應的任務。

2、初始化EventExecutor數組children
在MultithreadEventExecutorGroup的構造方法中我們看到,EventExecutor數組children初始化時是通過newChild(executor, args)實現的,而newChild的在MultithreadEventExecutorGroup中是個抽象方法

protected abstract EventExecutor newChild(Executor executor, Object... args) throws Exception;

根據最開始的類繼承結構圖,我們在NioEventLoopGroup中找到了newChild的實現

@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
        ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}

所以從此newChild的實現中,我們可以看出MultithreadEventExecutorGroup的children,其實就是對應的一組NioEventLoop對象。 關于NioEventLoop下一篇會作詳細介紹。

3、根據我們指定的選擇器工廠,綁定NioEventLoop數組對象

chooser = chooserFactory.newChooser(children);

在前面的構造方法中,我們指定了chooserFactory為DefaultEventExecutorChooserFactory,在此工廠內部,會根據children數組的長度來動態選擇選擇器對象,用于選擇下一個可執行的EventExecutor,也就是NioEventLoop。

@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
    if (isPowerOfTwo(executors.length)) {
        return new PowerOfTwoEventExecutorChooser(executors);
    } else {
        return new GenericEventExecutorChooser(executors);
    }
}

至此,NioEventLoopGroup初始化完成了。

通過上面的代碼分析,在NioEventLoopGroup初始化的過程中,其實就是初始化了一堆可執行的Executor數組,然后根據某種chooser策略,來選擇下一個可用的executor。

我們再回顧總結一下: 
1、NioEventLoopGroup初始化時未指定線程數,那么會使用默認線程數,
即 線程數 = CPU核心數 * 2;
2、每個NioEventLoopGroup對象內部都有一組可執行的NioEventLoop(NioEventLoop對象內部包含的excutor對象為ThreadPerTaskExecutor類型)
3、每個NioEventLoopGroup對象都有一個NioEventLoop選擇器與之對應,其會根據NioEventLoop的個數,動態選擇chooser(如果是2的冪次方,則按位運算,否則使用普通的輪詢)

所以通過上面的分析,我們得出NioEventLoopGroup主要功能就是為了選擇NioEventLoop,而真正的重點就在NioEventLoop中,它是整個netty線程執行的關鍵。

下一篇我們將詳細介紹NioEventLoop,敬請期待。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68100.html

相關文章

  • Netty4.x 源碼實戰系列(二):服務端bind流程詳解

    摘要:對于,目前大家只知道是個線程組,其內部到底如何實現的,它的作用到底是什么,大家也都不太清楚,由于篇幅原因,這里不作詳細介紹,后面會有文章作專門詳解。 在上一篇《ServerBootstrap 與 Bootstrap 初探》中,我們已經初步的了解了ServerBootstrap是netty進行服務端開發的引導類。 且在上一篇的服務端示例中,我們也看到了,在使用netty進行網絡編程時,我...

    laoLiueizo 評論0 收藏0
  • Netty4.x 源碼實戰系列(一):ServerBootstrap 與 Bootstrap 初探

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

    BakerJ 評論0 收藏0
  • 【自己讀源碼Netty4.X系列(一) 啟動類概覽

    摘要:一些想法這個系列想開很久了,自己使用也有一段時間了,利用也編寫了一個簡單的框架,并運用到工作中了,感覺還不錯,趁著這段時間工作不是很忙,來分析一波源碼,提升下技術硬實力。 一些想法 這個系列想開很久了,自己使用netty也有一段時間了,利用netty也編寫了一個簡單的框架,并運用到工作中了,感覺還不錯,趁著這段時間工作不是很忙,來分析一波源碼,提升下技術硬實力。 結構 這里先看下net...

    qingshanli1988 評論0 收藏0
  • Netty4.x 源碼實戰系列(三):NioServerSocketChannel全剖析

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

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

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

    13651657101 評論0 收藏0

發表評論

0條評論

MSchumi

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<