<input id="0qass"><u id="0qass"></u></input>
  • <input id="0qass"><u id="0qass"></u></input>
  • <menu id="0qass"><u id="0qass"></u></menu>

    阿里二面:NIO為什么不適合文件上傳場景、如何優雅解決

    見字如面,我是威哥,一個從普通二本院校畢業,從未曾接觸分布式、微服務、高并發到通過技術分享實現職場蛻變,成長為RocketMQ社區優秀布道師、大廠資深架構師,出版《RocketMQ技術內幕》一書,在CSDN中記錄了我的成長歷程,歡迎大家關注我,隨時可私信我,一起交流進步。

    該系列已分別介紹了服務端、客戶端的啟動流程,本文將重點剖析Netty是如何封裝NIO的讀事件。

    溫馨提示:本文雖然是源碼分析,但強烈建議精讀,因為根據源碼闡述其背后的設計哲學,也用黑體進行了標注,請特別留意。

    在閱讀本篇文章之前,請稍微思考如下幾個問題:

    • NIO為什么不適合文件上傳等場景
    • NIO如何避免一個超大數據傳送的連接對其他請求的影響
    • NIO如何處理半關閉

    1、讀事件概述

    關于Read事件在SocketChannel與ServerSocketChannel所對應的操作不一樣,在SocketChannel中,則對應數據讀,而在ServerSocketChannel中則被被封裝成接受客戶端的連接請求。

    NIO read事件入口在NioEventLoop的processSelectedKey方法,截圖如下:
    在這里插入圖片描述
    其核心入口為UnSafe的read方法,關于UnSafe的類層次結構如下圖所示:
    在這里插入圖片描述

    • AbstractNioByteChannel$NioByteUnsafe#read
      SocketChannel對應的讀事件處理流程,即IO讀的處理實現。
    • AbstractNioMessageChannel$NioMessageUnsafe#read
      ServerSocketChannle對應的讀事件處理流程。

    接下來將分別介紹這兩個流程。

    2、IO讀事件從處理流程

    IO讀事件由AbstractNioByteChannel內部類AbstractNioUnsafe的read方法實現,接下來重點剖析該方法,從中窺探Netty對IO讀事件的處理。
    在這里插入圖片描述
    Step1:如果沒有開啟自動注冊讀事件,在每一次讀時間處理過后會取消讀事件,默認為自動注冊。

    溫馨提示:如果通道不注冊讀事件,將無法從通道中讀取數據,即無法處理請求或接受響應。

    如果沒有開啟自動讀事件,需要應用程序在需要的時候手動調用通道的read方法。

    取消讀事件,Netty基于NIO給出了非常標準的實現,基本可以當場模板代碼使用:
    在這里插入圖片描述
    其實現關鍵點:首先判斷鍵值對是否有效,然后通過位運算進行取消注冊。
    在這里插入圖片描述
    Step2:創建接受緩存區內存分配器,這里有兩個關鍵點:

    • maxMessagesPerRead
      每一個通道在一次讀事件處理過程中最多可以調用底層Socket進行讀取的次數,默認為16次,這里的設計哲學是避免一個通道需要讀取太多的數據,從而影響其他通道的數據讀,因為在一個事件選擇器中多個通道的讀事件是串行執行的。
    • RecvByteBufAllocator
      接受緩沖區的內存分配策略,分為分配固定大小(不夠時擴容)、動態變化(根據歷史分配的大小,動態條件合適的內存大小),這里主要的設計哲學是合理利用內存,并減少擴容,提高內存的分配效率與使用效率。

    在這里插入圖片描述
    Step3:循環處理讀事件,最多處理maxMessagePerRead。接下來探討一下單次讀事件的處理流程。
    在這里插入圖片描述
    Step4:進行一次IO讀處理,其處理有如下幾個關鍵點:

    • 首先分配一個ByteBuf,俗稱接收緩存區,用來存放從網絡中讀取的內容。
    • 獲取一下分配到的累積緩存區可寫的字節數,這個后面有妙用。
    • 調用底層網絡讀API從網卡中讀取數據,NIO的讀取實現如下所示:
      在這里插入圖片描述
      即調用NIO中的SocketChannel進行讀數據,其返回參數表示這次從網卡中讀取到的字節數。如果讀取到的字節少于0,則表示對端通道已關閉,己端也需要進行相應的處理,例如關閉通道。
    • 讀到一批數據后,會通過事件傳播機制向事件鏈中傳播channelRead事件,觸發后續對該批數據的處理。

    在這里插入圖片描述
    Step5:判斷該通道是否需要繼續讀,其基本依據如下:

    • 如果未開啟自動注冊讀事件,讀完一次之后將不再繼續讀取。
    • 如果本次讀取到的字節數小于接收緩存區,說明此刻網卡中沒有可讀數據,等下一次讀事件觸發再繼續讀。

    在這里插入圖片描述
    Step6:一次或多次讀操作結束后,會觸發一次讀完成事件,向整個事件鏈傳播。

    整個網絡讀處理流程就介紹到這了。

    3、接受連接處理流程

    在Netty中,服務端接收客戶端的連接請求(OP_ACCEPT),被封裝在channelRead 事件中,其代碼入口為:AbstractNioMessageChannel 的內部類NioMessageUnsafe的read方法。
    在這里插入圖片描述
    其大概的實現要點在前面已經介紹,這里主要看一下NioServerSocketChannel的doReadMessage。
    在這里插入圖片描述
    通過使用底層的NIO接受一個連接,并獲取NioSocketChannel對象。然后繼續該對象向下傳播channelRead事件,在后續的處理器中對該對象進行操作,例如將其注冊讀事件,從而觸發網絡的讀操作,關于NioSocketChannel如何綁定讀事件、注冊業務相關的事件監聽器機制已經在Netty進階:手把手教你如何編寫一個NIO服務端中詳細介紹,本文就不再重復。

    好了,本文就介紹到這里了,想必對開頭部門提出的問題有了自己的答案了吧,您的一鍵三連是對我最大的鼓勵,當然可以加筆者微信:dingwpmz,備注CSDN,共同交流探討。


    為了方便大家學習Netty,筆者將RocketMQ的網絡通信模塊抽取出一個通用的Netty開發框架,大家可以從github上下載,堪稱Netty界最強Hello World。
    在這里插入圖片描述
    下載鏈接:Netty通用開發框架github地址

    中間件興趣圈 CSDN認證博客專家 RocketMQ 資深架構師 中間件興愛好者
    微信搜一搜【中間件興趣圈】,回復關鍵字[PDF]可獲取大量學習資料?!禦ocketMQ技術內幕》作者、CSDN2020博客之星第二名。目前就職于中通快遞研發中心擔任資深架構師,負責消息中間件與全鏈路壓測的實施與落地。歡迎大家加我個人微信:dingwpmz,拉您入技術交流群,共同發展,抱團取暖。擅長JAVA編程,對主流中間件RocketMQ、Dubbo、ElasticJob、Netty、Sentienl、Mybatis、Mycat等中間件有深入研究。
    相關推薦
    ??2020 CSDN 皮膚主題: 成長之路 設計師:Amelia_0503 返回首頁
    實付 79.00元
    使用余額支付
    點擊重新獲取
    掃碼支付
    錢包余額 0

    抵扣說明:

    1.余額是錢包充值的虛擬貨幣,按照1:1的比例進行支付金額的抵扣。
    2.余額無法直接購買下載,可以購買VIP、C幣套餐、付費專欄及課程。

    余額充值
    多乐彩