一、前言
應用程序最常使用的 IO 資源,主要包括磁盤 IO 和網絡 IO。由於現在的 SSD 的速度越來越快,對於本地磁盤的讀寫,異步的意義越來越小。所以,使用異步設計的方法來提升 IO 性能,我們更加需要關注的問題是,如何來實現高性能的異步網絡傳輸。
二、理想的異步網絡框架
大部分語言提供的網絡通信基礎類庫都是同步的。一個 TCP 連接建立后,用戶代碼會獲得一個用於收發數據的通道。每個通道會在內存中開辟兩片區域用於收發數據的緩存。
發送數據的過程比較簡單,我們直接往這個通道里面來寫入數據就可以了。用戶代碼在發送時寫入的數據會暫存在緩存中,然后操作系統會通過網卡,把發送緩存中的數據傳輸到對端的服務器上。只要這個緩存不滿,或者說,我們發送數據的速度沒有超過網卡傳輸速度的上限,那這個發送數據的操作耗時,只不過是一次內存寫入的時間,這個時間是非常快的。所以,發送數據的時候同步發送就可以了,沒有必要異步。
比較麻煩的是接收數據。對於數據的接收方來說,它並不知道什么時候會收到數據。那我們能直接想到的方法就是,用一個線程阻塞在那兒等着數據,當有數據到來的時候,操作系統會先把數據寫入接收緩存,然后給接收數據的線程發一個通知,線程收到通知后結束等待,開始讀取數據。處理完這一批數據后,繼續阻塞等待下一批數據到來,這樣周而復始地處理收到的數據。
這就是同步網絡 IO 的模型。同步網絡 IO 模型在處理少量連接的時候,是沒有問題的。但是如果要同時處理非常多的連接,同步的網絡 IO 模型就有點兒力不從心了。因為,每個連接都需要阻塞一個線程來等待數據,大量的連接數就會需要相同數量的數據接收線程。當這些 TCP 連接都在進行數據收發的時候,會導致有大量的線程來搶占 CPU 時間,造成頻繁的 CPU 上下文切換,導致 CPU 的負載升高,整個系統的性能就會比較慢。
我們希望達到的效果,無非就是,只用少量的線程就能處理大量的連接,有數據到來的時候能第一時間處理就可以了。
對於開發者來說,最簡單的方式就是,事先定義好收到數據后的處理邏輯,把這個處理邏輯作為一個回調方法,在連接建立前就通過框架提供的 API 設置好。當收到數據的時候,由框架自動來執行這個回調方法就好了。
三、異步網絡框架
1、使用 Netty 來實現異步網絡通信(java)
真正需要業務代碼來實現的就兩個部分:一個是把服務初始化並啟動起來,還有就是,實現收發消息的業務邏輯 MyHandler。而像線程控制、緩存管理、連接管理這些異步網絡 IO 中通用的、比較復雜的問題,Netty 已經自動幫你處理好了。所以,非常多的開源項目使用 Netty 作為其底層的網絡 IO 框架,並不是沒有原因的。
在這種設計中,Netty 自己維護一組線程來執行數據收發的業務邏輯。如果說,你的業務需要更靈活的實現,自己來維護收發數據的線程,可以選擇更加底層的 Java NIO。其實,Netty 也是基於 NIO 來實現的。
2、使用 NIO 來實現異步網絡通信(java)
四、總結
傳統的同步網絡 IO,一般采用的都是一個線程對應一個 Channel 接收數據,很難支持高並發和高吞吐量。這個時候,我們需要使用異步的網絡 IO 框架來解決問題。
- 了解了異步網絡傳輸的原理
- 用Netty來舉例是因為Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序
- rocketMQ的底層就是用Netty實現的。