網絡通信在RPC調用中起到什么作用呢?RPC是解決進程間通信的一種方式。一次RPC調用,本質就是服務消費者與服務提供者間的一次網絡信息交換的過程。服務調用者通過網絡IO發送一條請求消息,服務提供者接收并解析,處理完相關的業務邏輯之后,再發送一條響應消息給服務調用者,服務調用者接收并解析響應消息,處理完相關的響應邏輯,一次RPC調用便結束了??梢哉f,網絡通信是整個RPC調用流程的基礎。
(資料圖片)
兩臺PC機之間網絡通信,就是兩臺PC機對網絡IO的操作。
同步阻塞IO、同步非阻塞IO(NIO)、IO多路復用和異步非阻塞IO(AIO)。只有AIO為異步IO,其他都是同步IO。
1.1 同步阻塞I/O(BIO)Linux默認所有socket都是blocking。
應用進程發起IO系統調用后,應用進程被阻塞,轉到內核空間處理。之后,內核開始等待數據,等待到數據后,再將內核中的數據拷貝到用戶內存中,整個IO處理完畢后返回進程。最后應用的進程解除阻塞狀態,運行業務邏輯。
系統內核處理IO操作分為兩階段:
?等待數據系統內核在等待網卡接收到數據后,把數據寫到內核中?拷貝數據系統內核在獲取到數據后,將數據拷貝到用戶進程的空間在這兩個階段,應用進程中IO操作的線程會一直都處于阻塞狀態,若基于Java多線程開發,每個IO操作都要占用線程,直至IO操作結束。
用戶線程發起read調用后就阻塞了,讓出CPU。內核等待網卡數據到來,把數據從網卡拷貝到內核空間,接著把數據拷貝到用戶空間,再把用戶線程叫醒。
1.2 IO多路復用(IO multiplexing)高并發場景中使用最為廣泛的一種IO模型,如Java的NIO、Redis、Nginx的底層實現就是此類IO模型的應用:
?多路,即多個通道,即多個網絡連接的IO?復用,多個通道復用在一個復用器多個網絡連接的IO可注冊到一個復用器(select),當用戶進程調用select,整個進程會被阻塞。同時,內核會“監視”所有select負責的socket,當任一socket中的數據準備好了,select就會返回。這個時候用戶進程再調用read操作,將數據從內核中拷貝到用戶進程。
當用戶進程發起select調用,進程會被阻塞,當發現該select負責的socket有準備好的數據時才返回,之后才發起一次read,整個流程比阻塞IO要復雜,似乎更浪費性能。但最大優勢在于,用戶可在一個線程內同時處理多個socket的IO請求。用戶可注冊多個socket,然后不斷調用select讀取被激活的socket,即可達到在同一個線程內同時處理多個IO請求的目的。而在同步阻塞模型中,必須通過多線程實現。
好比我們去餐廳吃飯,這次我們是幾個人一起去的,我們專門留了一個人在餐廳排號等位,其他人就去逛街了,等排號的朋友通知我們可以吃飯了,我們就直接去享用。
本質上多路復用還是同步阻塞。
1.3 為何阻塞IO,IO多路復用最常用?網絡IO的應用上,需要的是系統內核的支持及編程語言的支持。
大多系統內核都支持阻塞IO、非阻塞IO和IO多路復用,但像信號驅動IO、異步IO,只有高版本Linux系統內核支持。
無論C++還是Java,在高性能的網絡編程框架都是基于Reactor模式,如Netty,Reactor模式基于IO多路復用。非高并發場景,同步阻塞IO最常見。
應用最多的、系統內核與編程語言支持最為完善的,便是阻塞IO和IO多路復用,滿足絕大多數網絡IO應用場景。
1.4 RPC框架選擇哪種網絡IO模型?IO多路復用適合高并發,用較少進程(線程)處理較多socket的IO請求,但使用難度較高。
阻塞IO每處理一個socket的IO請求都會阻塞進程(線程),但使用難度較低。在并發量較低、業務邏輯只需要同步進行IO操作的場景下,阻塞IO已滿足需求,并且不需要發起select調用,開銷比IO多路復用低。
RPC調用大多數是高并發調用,綜合考慮,RPC選擇IO多路復用。最優框架選擇即基于Reactor模式實現的框架Netty。Linux下,也要開啟epoll提升系統性能。
2 零拷貝(Zero-copy)2.1 網絡IO讀寫流程應用進程的每次寫操作,都把數據寫到用戶空間的緩沖區,CPU再將數據拷貝到系統內核緩沖區,再由DMA將這份數據拷貝到網卡,由網卡發出去。一次寫操作數據要拷貝兩次才能通過網卡發送出去,而用戶進程讀操作則是反過來,數據同樣會拷貝兩次才能讓應用程序讀到數據。
應用進程一次完整讀寫操作,都要在用戶空間與內核空間中來回拷貝,每次拷貝,都要CPU進行一次上下文切換(由用戶進程切換到系統內核,或由系統內核切換到用戶進程),這樣是不是很浪費CPU和性能呢?那有沒有什么方式,可以減少進程間的數據拷貝,提高數據傳輸的效率呢?
這就要零拷貝:取消用戶空間與內核空間之間的數據拷貝操作,應用進程每一次的讀寫操作,都讓應用進程向用戶空間寫入或讀取數據,就如同直接向內核空間寫或讀數據一樣,再通過DMA將內核中的數據拷貝到網卡,或將網卡中的數據copy到內核。
2.2 實現是不是用戶空間與內核空間都將數據寫到一個地方,就不需要拷貝了?想到虛擬內存嗎?
虛擬內存
零拷貝有兩種實現:
mmap+write通過虛擬內存來解決。
sendfileNginx sendfile
3 Netty零拷貝RPC框架在網絡通信框架的選型基于Reactor模式實現的框架,如Java首選Netty。那Netty有零拷貝機制嗎?Netty框架中的零拷貝和我之前講的零拷貝又有什么不同呢?
上節的零拷貝是os層的零拷貝,為避免用戶空間與內核空間之間的數據拷貝操作,可提升CPU利用率。
而Netty零拷貝不大一樣,他完全站在用戶空間,即JVM上,偏向于數據操作的優化。
Netty這么做的意義傳輸過程中,RPC不會把請求參數的所有二進制數據整體一下子發送到對端機器,中間可能拆分成好幾個數據包,也可能合并其他請求的數據包,所以消息要有邊界。一端的機器收到消息后,就要對數據包處理,根據邊界對數據包進行分割和合并,最終獲得一條完整消息。
那收到消息后,對數據包的分割和合并,是在用戶空間完成,還是在內核空間完成的呢?
當然是在用戶空間,因為對數據包的處理工作都是由應用程序來處理的,那么這里有沒有可能存在數據的拷貝操作?可能會存在,當然不是在用戶空間與內核空間之間的拷貝,是用戶空間內部內存中的拷貝處理操作。Netty的零拷貝就是為了解決這個問題,在用戶空間對數據操作進行優化。
那么Netty是怎么對數據操作進行優化的呢?
?Netty 提供了 CompositeByteBuf 類,它可以將多個 ByteBuf 合并為一個邏輯上的 ByteBuf,避免了各個 ByteBuf 之間的拷貝。?ByteBuf 支持 slice 操作,因此可以將 ByteBuf 分解為多個共享同一個存儲區域的 ByteBuf,避免了內存的拷貝。?通過 wrap 操作,我們可以將 byte[] 數組、ByteBuf、ByteBuffer 等包裝成一個 Netty ByteBuf 對象, 進而避免拷貝操作。Netty框架中很多內部的ChannelHandler實現類,都是通過CompositeByteBuf、slice、wrap操作來處理TCP傳輸中的拆包與粘包問題的。
Netty解決用戶空間與內核空間之間的數據拷貝Netty 的 ByteBuffer 采用 Direct Buffers,使用堆外直接內存進行Socket的讀寫操作,最終的效果與我剛才講解的虛擬內存所實現的效果一樣。
Netty 還提供 FileRegion 中包裝 NIO 的 FileChannel.transferTo() 方法實現了零拷貝,這與Linux 中的 sendfile 方式在原理一樣。
4 總結零拷貝帶來的好處就是避免沒必要的CPU拷貝,讓CPU解脫出來去做其他的事,同時也減少了CPU在用戶空間與內核空間之間的上下文切換,從而提升了網絡通信效率與應用程序的整體性能。
Netty零拷貝與os的零拷貝有別,Netty零拷貝偏向于用戶空間中對數據操作的優化,這對處理TCP傳輸中的拆包粘包問題有重要意義,對應用程序處理請求數據與返回數據也有重要意義。
X 關閉
X 關閉
- 15G資費不大降!三大運營商誰提供的5G網速最快?中國信通院給出答案
- 2聯想拯救者Y70發布最新預告:售價2970元起 迄今最便宜的驍龍8+旗艦
- 3亞馬遜開始大規模推廣掌紋支付技術 顧客可使用“揮手付”結賬
- 4現代和起亞上半年出口20萬輛新能源汽車同比增長30.6%
- 5如何讓居民5分鐘使用到各種設施?沙特“線性城市”來了
- 6AMD實現連續8個季度的增長 季度營收首次突破60億美元利潤更是翻倍
- 7轉轉集團發布2022年二季度手機行情報告:二手市場“飄香”
- 8充電寶100Wh等于多少毫安?鐵路旅客禁止、限制攜帶和托運物品目錄
- 9好消息!京東與騰訊續簽三年戰略合作協議 加強技術創新與供應鏈服務
- 10名創優品擬通過香港IPO全球發售4100萬股 全球發售所得款項有什么用處?