首頁 Java java教程 Java中關於IO復用的圖文詳解

Java中關於IO復用的圖文詳解

May 28, 2017 am 09:23 AM

這篇文章主要介紹了Java IO復用的相關知識,非常不錯,具有參考借鑒價值,需要的的朋友參考下吧

對於伺服器的並發處理能力,我們需要的是:每一毫秒伺服器都能及時處理這一毫秒內收到的數百個不同TCP連接上的報文,與此同時,可能伺服器上還有數以十萬計的最近幾秒沒有收發任何報文的相對不活躍連線。同時處理多個並行發生事件的連接,簡稱為並發;同時處理萬計、十萬計的連接,則是高並發。伺服器的並發程式所追求的就是處理的並發連線數目無限大,同時維持著高效率使用CPU等資源,直到實體資源先耗盡。

並發程式設計有很多種實作模型,最簡單的就是與「執行緒」捆綁,1個執行緒處理1個連線的全部生命週期。優點:這個模型夠簡單,它可以實現複雜的業務場景,同時,執行緒個數是可以遠大於CPU個數的。然而,線程個數又不是可以無限增大的,為什麼呢?因為線程什麼時候執行是由操作系統內核調度演算法決定的,調度演算法並不會考慮某個線程可能只是為了一個連接服務的,它會做大一統的玩法:時間片到了就執行一下,哪怕這個線程一執行就會不得不繼續睡眠。這樣來回的喚醒、睡眠線程在次數不多的情況下,是廉價的,但如果操作系統的線程總數很多時,它就是昂貴的(被放大了),因為這種技術性的調度損耗會影響到線程上執行的業務代碼的時間。舉個例子,這時大部分擁有不活躍連線的執行緒就像我們的國企,它們執行效率太低了,它總是喚醒就睡眠在做無用功,而它喚醒爭到CPU資源的同時,就意味著處理活躍連線的民企執行緒減少獲得了CPU的機會,CPU是核心競爭力,它的無效率進而影響了GDP總吞吐量。我們所追求的是並發處理數十萬連接,當幾千個線程出現時,系統的執行效率就已經無法滿足高並發了。

對高並發編程,目前只有一種模型,也是本質上唯一有效的玩法。連線上的消息處理,可分為兩個階段:等待訊息準備好、訊息處理。當使用預設的阻塞套接字時(例如上面提到的1個執行緒捆綁處理1個連線),往往是把這兩個階段合而為一,這樣操作套接字的程式碼所在的執行緒就得睡眠來等待訊息準備好,這導致了高並發下執行緒會頻繁的睡眠、喚醒,從而影響了CPU的使用效率。

高並發程式設計方法當然就是把兩個階段分開處理。即,等待訊息準備好的程式碼段,與處理訊息的程式碼段是分離的。當然,這也要求套接字必須是非阻塞的,否則,處理訊息的程式碼片段很容易導致條件不滿足時,所在執行緒又進入了睡眠等待階段。那麼問題來了,等待訊息準備好這個階段怎麼實現?它畢竟還是等待,這意味著線程還是要睡眠的!解決辦法就是,主動查詢,或讓1個執行緒為所有連線而等待!這就是IO多路復用了。多路復用就是處理等待訊息準備好這件事的,但它可以同時處理多個連線!它也可以“等待”,所以它也可能導致線程睡眠,然而這不要緊,因為它一對多、它可以監控所有連接。這樣,當我們的執行緒被喚醒執行時,就一定是有一些連線準備好被我們的程式碼執行了,這是有效率的!沒有那麼多線程都在爭搶處理「等待訊息準備好」階段,整個世界終於清淨了!
多重化有很多種實現,在linux上,2.4核心前主要是select和poll,現在主流是epoll,它們的使用方法似乎很不同,但本質是一樣的。

效率卻也不同,這也是epoll完全取代了select的原因。

簡單的談下epoll為何會取代select。

前面提到過,高並發的核心解決方案是1個線程處理所有連接的“等待訊息準備好”,這一點上epoll和select是無爭議的。但select預估錯誤了一件事,就像我們開篇所說,當數十萬並發連接存在時,可能每一毫秒只有數百個活躍的連接,同時其餘數十萬連接在這一毫秒是非活躍的。 select的使用方法是這樣的:
傳回的活躍連線 ==select(全部待監控的連線)

什麼時候會呼叫select方法呢?在你認為需要找出有報文到達的活躍連結時,就應該呼叫。所以,呼叫select在高並發時是會被頻繁呼叫的。這樣,這個頻繁調用的方法就很有必要看看它是否有效率,因為,它的輕微效率損失都會被「頻繁」二字所放大。它有效率損失嗎?顯而易見,全部待監控連接是數以十萬計的,返回的只是數百個活躍連接,這本身就是無效率的表現。被放大後就會發現,處理並發上萬個連結時,select就完全力不從心了。
看幾個圖。當並發連結為一千以下,select的執行次數不算頻繁,與epoll似乎並無多少差距: 

然而,並發數一旦上去, select的缺點被「執行頻繁」無限放大了,且並發數越多越明顯:

再來說說epoll是如何解決的。它很聰明的用了3個方法來實現select方法要做的事:

新建的epoll描述符==epoll_create()

epoll_ctrl(epoll描述符,添加或刪除所有待監控的連接)

返回的活躍連接==epoll_wait(epoll描述子)

這麼做的好處主要是:分清了頻繁調用和不頻繁呼叫的操作。例如,epoll_ctrl是不太頻繁呼叫的,而epoll_wait是非常頻繁地呼叫的。這時,epoll_wait卻幾乎沒有入參,這比select的效率高出一大截,而且,它也不會隨著並發連接的增加使得入參越發多起來,導致內核執行效率下降。

epoll是怎麼實現的呢?其實很簡單,從這3個方法就可以看出,它比select聰明的避免了每次頻繁調用「哪些連接已經處在訊息準備好階段」的epoll_wait時,是不需要把所有待監控連接傳入的。這意味著,它在內核態維護了一個資料結構保存所有待監控的連線。這個資料結構就是一棵紅黑樹,它的結點的增加、減少是透過epoll_ctrl來完成的。它是非常簡單的: 

圖中左下方的紅黑樹由所有待監控的連結所構成。左上方的鍊錶,同是目前所有活躍的連結。於是,epoll_wait執行時只是檢查左上方的鍊錶,並返回左上方鍊錶中的連接給用戶。這樣,epoll_wait的執行效率能不高嗎?

最後,再看看epoll提供的2種玩法ET和LT,也就是翻譯過來的邊緣觸發和水平觸發。其實這兩個中文名字倒也有些貼切。這2種使用方式針對的仍然是效率問題,只不過變成了epoll_wait返回的連接如何能夠更準確些。

例如,我們需要監控一個連線的寫緩衝區是否空閒,滿足「可寫」時我們就可以從使用者狀態將回應呼叫write傳送給客戶端 。但是,或連線可寫時,我們的「回應」內容還在磁碟上呢,此時若是磁碟讀取還沒完成呢?肯定不能使線程阻塞的,那麼就不發送回應了。但是,下次epoll_wait時可能又把這個連線回傳給你了,你還得檢查下是否要處理。可能,我們的程式有另一個模組專門處理磁碟IO,它會在磁碟IO完成時再發送回應。那麼,每次epoll_wait都回傳這個「可寫」的、卻無法立刻處理的連接,是否符合使用者預期呢?

於是,ET和LT模式就應運而生了。 LT是每次滿足期待狀態的連接,都得在epoll_wait中返回,所以它一視同仁,都在一條水平線上。 ET則不然,它傾向於更精確的返回連接。在上面的例子中,連接第一次變成可寫後,若是程式未寫入連接上寫入任何數據,那麼下一次epoll_wait是不會回傳這個連接的。 ET叫做 邊緣觸發,就是指,只有連線從一個狀態轉到另一個狀態時,才會觸發epoll_wait回傳它。可見,ET的編程要複雜不少,至少應用程式要小心的防止epoll_wait的返回的連接出現:可寫時未寫數據後卻期待下一次“可寫”、可讀時未讀盡數據卻期待下一次「可讀」。

當然,從一般應用場景上它們性能是不會有什麼大的差距的,ET可能的優點是,epoll_wait的調用次數會減少一些,某些場景下連接在不必要喚醒時不會被喚醒(此喚醒指epoll_wait返回)。但如果像我上面舉例所說的,有時它不單純是網路問題,跟應用場景相關。當然,大部分開源框架都是基於ET寫的,框架嘛,它追求的是純技術問題,當然力求盡善盡美

#

以上是Java中關於IO復用的圖文詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1662
14
CakePHP 教程
1419
52
Laravel 教程
1313
25
PHP教程
1263
29
C# 教程
1236
24
突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP:網絡開發的關鍵語言 PHP:網絡開發的關鍵語言 Apr 13, 2025 am 12:08 AM

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP與Python:了解差異 PHP與Python:了解差異 Apr 11, 2025 am 12:15 AM

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP與其他語言:比較 PHP與其他語言:比較 Apr 13, 2025 am 12:19 AM

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP與Python:核心功能 PHP與Python:核心功能 Apr 13, 2025 am 12:16 AM

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

Java程序查找膠囊的體積 Java程序查找膠囊的體積 Feb 07, 2025 am 11:37 AM

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

PHP的影響:網絡開發及以後 PHP的影響:網絡開發及以後 Apr 18, 2025 am 12:10 AM

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

PHP:許多網站的基礎 PHP:許多網站的基礎 Apr 13, 2025 am 12:07 AM

PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。

See all articles