精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

探究Redis網絡模型究竟有多強大(下)

jf_78858299 ? 來源:蟬沐風的碼場 ? 作者:蟬沐風 ? 2023-03-03 09:50 ? 次閱讀

4. 非阻塞I/O(NonBlocking I/O)

上文花了太多的筆墨描述BIO,接下來的非阻塞IO我們只抓主要矛盾,其余參考BIO即可。

如果你看過其他介紹非阻塞IO的文章,下面這個圖片你多少會有點眼熟。

圖片

NIO模型

非阻塞IO指的是進程發起系統調用之后,內核不會將進程投入睡眠,而是會立即返回一個結果,這個結果可能恰好是我們需要的數據,又或者是某些錯誤。

你可能會想,這種非阻塞帶來的輪詢有什么用呢?大多數都是空輪詢,白白浪費CPU而已,還不如讓進程休眠來的合適。

4.1 Java的非阻塞實現

這個問題暫且擱置一下,我們先看Java在語法層面是如何提供非阻塞功能的,細節慢慢聊。

public class NoBlockingServer {

    public static List<SocketChannel> channelList = new ArrayList<>();

    public static void main(String[] args) throws InterruptedException {

        try {
            // 相當于serverSocket
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 將監聽socket設置為非阻塞
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().bind(new InetSocketAddress(8099));
            while (true) {
                // 這里將不再阻塞
                SocketChannel socketChannel = serverSocketChannel.accept();

                if (socketChannel != null) {
                    // 將連接socket設置為非阻塞
                    socketChannel.configureBlocking(false);
                    channelList.add(socketChannel);
                } else {
                    System.out.println("沒有客戶端連接!!!");
                }

                for (SocketChannel client : channelList) {
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    // read也不阻塞
                    int num = client.read(byteBuffer);
                    if (num > 0) {
                        System.out.println("收到客戶端【" + client.socket().getPort() + "】數據:" + new String(byteBuffer.array()));
                    } else {
                        System.out.println("等待客戶端【" + client.socket().getPort() + "】寫數據");
                    }
                }

                // 加個睡眠是為了避免strace產生大量日志,否則不好追蹤
                Thread.sleep(1000);

            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Java提供了新的APIServerSocketChannel以及SocketChannel,相當于BIO中的ServerSocketSocket。此外,通過下面兩行的配置,將監聽socket和連接socket設置為非阻塞。

// 將監聽socket設置為非阻塞
serverSocketChannel.configureBlocking(false);

// 將連接socket設置為非阻塞
socketChannel.configureBlocking(false);

我們上文強調過, Java自身并沒有將socket設置為非阻塞的本事,一定是在某個時間點上,操作系統內核提供了這個功能,才使得Java設計出了新的API來提供非阻塞功能

之所以需要上面兩行代碼的顯式設置,也恰好說明了內核是默認將socket設置為阻塞狀態的,需要非阻塞,就得額外調用其他系統調用。我們通過man命令查看一下socket()這個方法(截圖的中間省略了一部分內容):

man 2 socket

f336ca741589b768db4fecb030b6b432.png

image-20221225144028751

我們可以看到socket()函數提供了SOCK_NONBLOCK這個類型,可以通過fcntl()這個方法將socket從默認的阻塞修改為非阻塞,不管是對監聽socket還是連接socket都是一樣的。

4.2 Java的非阻塞解釋

現在解釋上面提到的問題:這種非阻塞帶來的輪詢有什么用?觀察一下上面的代碼就可以發現,我們全程只使用了1個main線程就解決了所有客戶端的連接以及所有客戶端的讀寫操作。

serverSocketChannel.accept();會立即返回調用結果。

返回的結果如果是一個SocketChannel對象(系統調用底層就是個socket描述符),說明有客戶端連接,這個SocketChannel就表示了這個連接;然后利用socketChannel.configureBlocking(false);將這個連接socket設置為非阻塞。這個設置非常重要,設置之后對連接socket所有的讀寫操作都變成了非阻塞,因此接下來的client.read(byteBuffer);并不會阻塞while循環,導致新的客戶端無法連接。再之后將該連接socket加入到channelList隊列中。

如果返回的結果為空(底層系統調用返回了錯誤),就說明現在還沒有新的客戶端要連接監聽socket,因此程序繼續向下執行,遍歷channelList隊列中的所有連接socket,對連接socket進行讀操作。而讀操作也是非阻塞的,會理解返回一個整數,表示讀到的字節數,如果>0,則繼續進行下一步的邏輯處理;否則繼續遍歷下一個連接socket。

下面給出一張accept()返回一個連接socket情況下的動圖,希望對大家理解整個流程有幫助。

4.3 掀開非阻塞IO的底褲

我將上面的程序在CentOS下再次用strace程序追蹤一下,具體步驟不再贅述,下面是out日志文件的內容(我忽略了絕大多數沒用的)。

圖片

非阻塞IO的系統調用分析

4.4 非阻塞IO總結

圖片

NIO模型

再放一遍這個圖,有一個細節需要大家注意,系統調用向內核要數據時,內核的動作分成兩步:

  1. 等待數據(從網卡緩沖區拷貝到內核緩沖區)
  2. 拷貝數據(數據從內核緩沖區拷貝到用戶空間)

只有在第1步時,系統調用是非阻塞的,第2步進程依然需要等待這個拷貝過程,然后才能返回,這一步是阻塞的。

非阻塞IO模型僅用一個線程就能處理所有操作,對比BIO的一個客戶端需要一個線程而言進步還是巨大的。但是他的致命問題在于會不停地進行系統調用,不停的進行accept(),不停地對連接socket進行read()操作,即使大部分時間都是白忙活。要知道,系統調用涉及到用戶空間和內核空間的多次轉換,會嚴重影響整體性能。

所以,一個自然而言的想法就是,能不能別讓進程瞎輪詢。

比如有人告訴進程監聽socket是不是被連接了,有的話進程再執行accept();比如有人告訴進程哪些連接socket有數據從客戶端發送過來了,然后進程只對有數據的連接socket進行read()

這個方案就是 I/O多路復用

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • IO
    IO
    +關注

    關注

    0

    文章

    435

    瀏覽量

    39080
  • 非阻塞
    +關注

    關注

    0

    文章

    13

    瀏覽量

    2165
  • Redis
    +關注

    關注

    0

    文章

    371

    瀏覽量

    10846
收藏 人收藏

    評論

    相關推薦

    小米5的那顆核心,究竟有多強

    小米5的那顆核心,究竟有多強自從小米在2011年崛起后,高通的驍龍系列處理器就逐漸成為了市面上旗艦手機的主流處理器,從蝎子核心到環蛇核心,再到現在的驍龍810,高通一直在進步。那么,這款驍龍810
    發表于 06-01 19:35

    Redis Stream應用案例

    今天介紹的主角——Redis Stream,本身就是起源于IRC中一個用戶的idea。IRC的模型如下,在某個IRC頻道中的用戶,既可以向所有的其他用戶自由的發送消息,也可以接收其他所有用戶發送
    發表于 06-26 17:15

    液晶PC與液晶電視究竟有什么區別?

    為什么要選擇液晶?液晶PC與液晶電視究竟有什么區別?如何選擇液晶PC與液晶電視?
    發表于 06-07 06:13

    請問一RFID與NFC究竟有什么關系?

    RFID與NFC究竟有什么關系?
    發表于 06-15 07:06

    面向列的HBase存儲結構究竟有什么樣的不同之處呢?

    HBase是什么?HBase的存儲結構究竟是怎樣的呢?面向列的HBase存儲結構究竟有什么樣的不同之處呢?
    發表于 06-16 06:52

    請問一芯片制造究竟有多難?

    請問一芯片制造究竟有多難?
    發表于 06-18 06:53

    PCI-E4.0究竟有什么優勢?

    PCI-E4.0究竟有什么優勢?PCI-E究竟指的是什么呢?
    發表于 06-18 06:54

    內存時序究竟有多重要呢?究竟該如何去選擇內存條呢?

    內存時序究竟有多重要呢?究竟該如何去選擇內存條呢?DDR內存時序是高一些好還是低一些好?
    發表于 06-18 08:20

    定時器中斷類型探究 精選資料分享

     一直在用的stm32定時器的中斷都是TIM_IT_Update更新中斷,也沒問為什么,直到碰到有人使用TIM_IT_CC1中斷,才想到這定時器的中斷類型究竟有什么區別,都怪當時學習stm32的時候
    發表于 08-13 06:28

    OpenPLC開源工業控制器究竟有何用處

    OpenPLC開源工業控制器有哪些優點?OpenPLC開源工業控制器有哪些功能?OpenPLC開源工業控制器究竟有何用處?
    發表于 09-02 07:42

    華為榮耀Magic今日發布:“未來”手機究竟有多強

    華為榮耀即將在12月16日發布最新的“未來”手機magic,關于這款手機的爆料在今日已經鋪天蓋地,今天,小編將為大家整理一,給大家一個榮耀Magic的基本判斷,看看這款旗艦究竟有多強力!
    發表于 12-16 09:34 ?3315次閱讀

    ibm的2nm芯片究竟有多強 2nm芯片對續航的影響

    全球首顆2nm芯片的問世對半導體行業影響重大,IBM通過與AMD、三星及GlobalFoundries等多家公司的合作,最終抵達了2nm芯片制程的節點,推出了2nm的測試芯片。那么這顆芯片究竟有多強呢?它對續航的影響又有多大呢?
    的頭像 發表于 06-23 09:35 ?2065次閱讀

    Molex莫仕連接器的功能究竟有多強大?看他們的行業應用你就知道了!

    KOYUELEC光與電子:Molex莫仕連接器的功能究竟有多強大?看他們的行業應用你就知道了!
    的頭像 發表于 12-31 12:30 ?1w次閱讀

    探究Redis網絡模型究竟有多強大(上)

    本文將從BIO開始介紹,經過NIO、多路復用,最終說回Redis的Reactor模型,力求詳盡。本文與其他文章的不同點主要在于:
    的頭像 發表于 03-03 09:46 ?430次閱讀
    <b class='flag-5'>探究</b><b class='flag-5'>Redis</b><b class='flag-5'>網絡</b><b class='flag-5'>模型</b><b class='flag-5'>究竟有多強大</b>(上)

    探究Redis網絡模型究竟有多強大(中)

    創建socket這一步和客戶端沒啥區別,不同的是這個socket我們稱之為 **等待連接socket(或監聽socket)** 。 #### 3.2.2 綁定端口號 `bind()`函數會將端口號寫入上
    的頭像 發表于 03-03 09:49 ?337次閱讀
    <b class='flag-5'>探究</b><b class='flag-5'>Redis</b><b class='flag-5'>網絡</b><b class='flag-5'>模型</b><b class='flag-5'>究竟有多強大</b>(中)