一、前言

  在學(xué)習(xí)了ChannelHandler和ChannelPipeline的有關(guān)細節(jié)后,接著學(xué)習(xí)Netty的EventLoop和線程模型。

二、EventLoop和線程模型

  2.1. 線程模型

  線程池可通過緩存和復(fù)用已有線程來提高系統(tǒng)性能,基本的緩沖池模式可描述如下:

    · 從池中空閑鏈表中選取線程,然后將其分配賦予給已提交的任務(wù)。

    · 當(dāng)線程完成工作時,該線程又返回至空閑鏈表,可再進行復(fù)用。

  該模式如下圖所示。

  photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

  池化和復(fù)用線程是針對每個任務(wù)都需要創(chuàng)建和銷毀線程的改進,但還是需要進行上下文切換,并且隨著線程數(shù)量的增加,其負擔(dān)也會增加。同時,在高并發(fā)下也會出現(xiàn)很多線程問題。

  2.2. EventLoop接口

  任何網(wǎng)絡(luò)框架的基本功能都是運行任務(wù)來處理在連接聲明周期中所發(fā)生的事件,相應(yīng)的編程結(jié)構(gòu)通常被稱為事件循環(huán)。事件循環(huán)的基本思想如下代碼所示,每個任務(wù)都是一個Runnable實例。  

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

while (!terminated) {
    List<Runnable> readyEvents = blockUntilEventsReady();    for (Runnable ev: readyEvents) {
        ev.run();
    }
}

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

  Netty的EventLoop是使用concurrency和networking兩個基本API的協(xié)作設(shè)計的一部分,Netty中的io.netty.util.concurrent 包基于JDK的java.util.concurrent包進行設(shè)計。另外,io.netty.channel包中的類也繼承它們,以便與其事件相關(guān)聯(lián),具體繼承關(guān)系如下圖所示。

  photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

  在這個模型中,EventLoop由一個永不改變的線程驅(qū)動,任務(wù)(Runnable或Callable)可以直接提交給EventLoop的實現(xiàn),以便立即執(zhí)行或有計劃地執(zhí)行。根據(jù)配置和可用內(nèi)核,可以創(chuàng)建多個EventLoops以優(yōu)化資源使用,并且可以為單個EventLoop分配服務(wù)多個通道。

  事件和任務(wù)以FIFO的方式被執(zhí)行,這通過保證以正確的順序處理字節(jié)內(nèi)容來消除數(shù)據(jù)損壞的可能性。

  1. Netty 4中的I/O和事件處理

  由I/O操作觸發(fā)的事件流過具有一個或多個ChannelHandler的ChannelPipeline時,傳播這些事件的方法調(diào)用可以由ChannelHandler攔截,并根據(jù)需要進行處理,根據(jù)事件的不同,需要進行不同的處理,但事件處理邏輯必須具有通用性和靈活性,以處理所有可能的用例,因此,在Netty 4中,所有的I/O操作和事件都由已分配給EventLoop的線程處理。

  2. Netty 3中的I/O處理

  以前版本中使用的線程模型僅保證入站(上游)事件將在所謂的I/O線程中執(zhí)行,所有出站(下游)事件由調(diào)用線程處理,其需要在ChannelHandlers中仔細同步出站事件,因為不可能保證多個線程不會同時嘗試訪問出站事件。

  2.3 任務(wù)調(diào)度

  有時需要讓一個任務(wù)稍后(延遲)或定期執(zhí)行,一個常見的用例是向遠程對等體發(fā)送心跳消息,以檢查連接是否仍然存在。

  1. JDK調(diào)度API

  在Java 5之前,任務(wù)調(diào)度基于java.util.Timer構(gòu)建,其使用后臺線程,與標(biāo)準(zhǔn)線程具有相同的限制,隨后,Java提供了ScheduledExecutorService接口,如下代碼在60S后執(zhí)行任務(wù)。  

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
ScheduledFuture<?> future = executor.schedule(    new Runnable() {
        @Override        public void run() {
            System.out.println("60 seconds later");
        }
    }, 60, TimeUnit.SECONDS);
    
executor.shutdown();

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

  2. 使用EventLoop調(diào)度任務(wù)

  ScheduledExecutorService實現(xiàn)有限制,如為管理池需要創(chuàng)建額外的線程,如果許多任務(wù)被調(diào)度,這可能會成為系統(tǒng)性能瓶頸。Netty通過使用Channel的EventLoop調(diào)度來解決這個問題,如下代碼所示?! ?/p>

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

Channel ch = ...
ScheduledFuture<?> future = ch.eventLoop().schedule(    new Runnable() {
    @Override    public void run() {
        System.out.println("60 seconds later");
    }
}, 60, TimeUnit.SECONDS);

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

  60秒后,Runnable實例將由分配給該Channel的EventLoop執(zhí)行。若想每隔60S執(zhí)行任務(wù),則需要做如下處理?! ?/p>

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

Channel ch = ...
ScheduledFuture<?> future = ch.eventLoop().scheduleAtFixedRate(    new Runnable() {
    @Override    public void run() {
        System.out.println("Run every 60 seconds");
    }
}, 60, 60, TimeUnit.Seconds);

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

  因為EventLoop繼承ScheduledExecutorService,因此可以調(diào)用ScheduledExecutorService的所有方法。

  2.4 實現(xiàn)細節(jié)

  1. 線程管理

  Netty的線程模型的優(yōu)越性能取決于確定當(dāng)前正在執(zhí)行的線程的身份,即是否為分配給當(dāng)前Channel及其EventLoop的線程。如果調(diào)用的是EventLoop的線程,那么直接執(zhí)行該代碼塊,否則,EventLoop調(diào)度一個任務(wù)以供稍后執(zhí)行,并將其放入內(nèi)部隊列中,當(dāng)處理下個事件時,會處理隊列中的事件,這解釋了任何線程為何可以直接與Channel交互,而不需要在ChannelHandler中同步。

  每個EventLoop都有自己的任務(wù)隊列,與其他EventLoop獨立,下圖顯示了EventLoop的執(zhí)行邏輯。

  photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

  不要把長時間的任務(wù)放在執(zhí)行隊列中,因為它將阻止任何其他任務(wù)在同一個線程上執(zhí)行。如果必須進行阻塞調(diào)用或執(zhí)行長時間運行的任務(wù),建議使用專用的EventExecutor。

  2. EventLoop/線程的分配

  為通道的I/O和事件提供服務(wù)的EventLoops包含在EventLoopGroup,EventLoops創(chuàng)建和分配的方式根據(jù)傳輸實現(xiàn)(異步和阻塞)而有所不同。

  · 異步傳輸。只使用少量的EventLoopGroup,在當(dāng)前的模型中其在通道中共享。這允許通道由最小數(shù)量的線程提供服務(wù),而不是為每個通道分配一個線程。下圖展示了包含三個EventLoop(每個EventLoop由一個線程驅(qū)動)的EventLoopGroup,EventLoopGroup創(chuàng)建時會直接分配EventLoops(及其線程),以確保它們在需要時可用,EventLoopGroup負責(zé)將EventLoop分配給每個新創(chuàng)建的通道,當(dāng)前的實現(xiàn)是使用循環(huán)方法實現(xiàn)均衡分配,相同的EventLoop可被分配給多個通道。

  photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

  一旦一個Channel被分配了一個EventLoop,它將在其生命周期中一直使用這個EventLoop(和相關(guān)聯(lián)的線程)。同時請注意EventLoop的分配對ThreadLocal影響,因為一個EventLoop通常驅(qū)動多個通道,多個通道的ThreadLocal也相同。

  · 阻塞傳輸。OIO的實現(xiàn)與異步傳輸?shù)膶崿F(xiàn)大不相同,其如下圖所示。

  photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

  每個通道將會分配一個EventLoop(以及相關(guān)線程),Channel的IO事件將由獨立的線程處理。

三、總結(jié)

  本篇博文講解了EventLoop及其線程模型,以及其與通道之間的關(guān)系,EventLoopGroup可對應(yīng)多個EventLoop,一個EventLoop對應(yīng)一個線程,一個EventLoop可對應(yīng)多個通道。也謝謝各位園友的觀看~

PS:如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”,將會是我不竭的動力! 
作者:leesf    掌控之中,才會成功;掌控之外,注定失敗。 
出處:http://www.cnblogs.com/leesf456/ 
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。 

http://www.cnblogs.com/leesf456/p/6902636.html