本篇文章為大家展示了一文帶你讀懂Java IO復用,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
對于服務器的并發(fā)處理能力,我們需要的是:每一毫秒服務器都能及時處理這一毫秒內(nèi)收到的數(shù)百個不同TCP連接上的報文,與此同時,可能服務器上還有數(shù)以十萬計的最近幾秒沒有收發(fā)任何報文的相對不活躍連接。同時處理多個并行發(fā)生事件的連接,簡稱為并發(fā);同時處理萬計、十萬計的連接,則是高并發(fā)。服務器的并發(fā)編程所追求的就是處理的并發(fā)連接數(shù)目無限大,同時維持著高效率使用CPU等資源,直至物理資源首先耗盡。
并發(fā)編程有很多種實現(xiàn)模型,最簡單的就是與“線程”捆綁,1個線程處理1個連接的全部生命周期。優(yōu)點:這個模型足夠簡單,它可以實現(xiàn)復雜的業(yè)務場景,同時,線程個數(shù)是可以遠大于CPU個數(shù)的。然而,線程個數(shù)又不是可以無限增大的,為什么呢?因為線程什么時候執(zhí)行是由操作系統(tǒng)內(nèi)核調(diào)度算法決定的,調(diào)度算法并不會考慮某個線程可能只是為了一個連接服務的,它會做大一統(tǒng)的玩法:時間片到了就執(zhí)行一下,哪怕這個線程一執(zhí)行就會不得不繼續(xù)睡眠。這樣來回的喚醒、睡眠線程在次數(shù)不多的情況下,是廉價的,但如果操作系統(tǒng)的線程總數(shù)很多時,它就是昂貴的(被放大了),因為這種技術(shù)性的調(diào)度損耗會影響到線程上執(zhí)行的業(yè)務代碼的時間。舉個例子,這時大部分擁有不活躍連接的線程就像我們的國企,它們執(zhí)行效率太低了,它總是喚醒就睡眠在做無用功,而它喚醒爭到CPU資源的同時,就意味著處理活躍連接的民企線程減少獲得了CPU的機會,CPU是核心競爭力,它的無效率進而影響了GDP總吞吐量。我們所追求的是并發(fā)處理數(shù)十萬連接,當幾千個線程出現(xiàn)時,系統(tǒng)的執(zhí)行效率就已經(jīng)無法滿足高并發(fā)了。
對高并發(fā)編程,目前只有一種模型,也是本質(zhì)上唯一有效的玩法。連接上的消息處理,可以分為兩個階段:等待消息準備好、消息處理。當使用默認的阻塞套接字時(例如上面提到的1個線程捆綁處理1個連接),往往是把這兩個階段合而為一,這樣操作套接字的代碼所在的線程就得睡眠來等待消息準備好,這導致了高并發(fā)下線程會頻繁的睡眠、喚醒,從而影響了CPU的使用效率。
高并發(fā)編程方法當然就是把兩個階段分開處理。即,等待消息準備好的代碼段,與處理消息的代碼段是分離的。當然,這也要求套接字必須是非阻塞的,否則,處理消息的代碼段很容易導致條件不滿足時,所在線程又進入了睡眠等待階段。那么問題來了,等待消息準備好這個階段怎么實現(xiàn)?它畢竟還是等待,這意味著線程還是要睡眠的!解決辦法就是,主動查詢,或者讓1個線程為所有連接而等待!這就是IO多路復用了。多路復用就是處理等待消息準備好這件事的,但它可以同時處理多個連接!它也可以“等待”,所以它也可能導致線程睡眠,然而這不要緊,因為它一對多、它可以監(jiān)控所有連接。這樣,當我們的線程被喚醒執(zhí)行時,就一定是有一些連接準備好被我們的代碼執(zhí)行了,這是有效率的!沒有那么多個線程都在爭搶處理“等待消息準備好”階段,整個世界終于清凈了!
多路復用有很多種實現(xiàn),在linux上,2.4內(nèi)核前主要是select和poll,現(xiàn)在主流是epoll,它們的使用方法似乎很不同,但本質(zhì)是一樣的。
效率卻也不同,這也是epoll完全替代了select的原因。
簡單的談下epoll為何會替代select。
前面提到過,高并發(fā)的核心解決方案是1個線程處理所有連接的“等待消息準備好”,這一點上epoll和select是無爭議的。但select預估錯誤了一件事,就像我們開篇所說,當數(shù)十萬并發(fā)連接存在時,可能每一毫秒只有數(shù)百個活躍的連接,同時其余數(shù)十萬連接在這一毫秒是非活躍的。select的使用方法是這樣的:
返回的活躍連接 ==select(全部待監(jiān)控的連接)
什么時候會調(diào)用select方法呢?在你認為需要找出有報文到達的活躍連接時,就應該調(diào)用。所以,調(diào)用select在高并發(fā)時是會被頻繁調(diào)用的。這樣,這個頻繁調(diào)用的方法就很有必要看看它是否有效率,因為,它的輕微效率損失都會被“頻繁”二字所放大。它有效率損失嗎?顯而易見,全部待監(jiān)控連接是數(shù)以十萬計的,返回的只是數(shù)百個活躍連接,這本身就是無效率的表現(xiàn)。被放大后就會發(fā)現(xiàn),處理并發(fā)上萬個連接時,select就完全力不從心了。
看幾個圖。當并發(fā)連接為一千以下,select的執(zhí)行次數(shù)不算頻繁,與epoll似乎并無多少差距:
然而,并發(fā)數(shù)一旦上去,select的缺點被“執(zhí)行頻繁”無限放大了,且并發(fā)數(shù)越多越明顯:
再來說說epoll是如何解決的。它很聰明的用了3個方法來實現(xiàn)select方法要做的事:
新建的epoll描述符==epoll_create()
epoll_ctrl(epoll描述符,添加或者刪除所有待監(jiān)控的連接)
返回的活躍連接 ==epoll_wait( epoll描述符 )
這么做的好處主要是:分清了頻繁調(diào)用和不頻繁調(diào)用的操作。例如,epoll_ctrl是不太頻繁調(diào)用的,而epoll_wait是非常頻繁調(diào)用的。這時,epoll_wait卻幾乎沒有入?yún)ⅲ@比select的效率高出一大截,而且,它也不會隨著并發(fā)連接的增加使得入?yún)⒃桨l(fā)多起來,導致內(nèi)核執(zhí)行效率下降。
epoll是怎么實現(xiàn)的呢?其實很簡單,從這3個方法就可以看出,它比select聰明的避免了每次頻繁調(diào)用“哪些連接已經(jīng)處在消息準備好階段”的 epoll_wait時,是不需要把所有待監(jiān)控連接傳入的。這意味著,它在內(nèi)核態(tài)維護了一個數(shù)據(jù)結(jié)構(gòu)保存著所有待監(jiān)控的連接。這個數(shù)據(jù)結(jié)構(gòu)就是一棵紅黑樹,它的結(jié)點的增加、減少是通過epoll_ctrl來完成的。它是非常簡單的:
圖中左下方的紅黑樹由所有待監(jiān)控的連接構(gòu)成。左上方的鏈表,同是目前所有活躍的連接。于是,epoll_wait執(zhí)行時只是檢查左上方的鏈表,并返回左上方鏈表中的連接給用戶。這樣,epoll_wait的執(zhí)行效率能不高嗎?
最后,再看看epoll提供的2種玩法ET和LT,即翻譯過來的邊緣觸發(fā)和水平觸發(fā)。其實這兩個中文名字倒也有些貼切。這2種使用方式針對的仍然是效率問題,只不過變成了epoll_wait返回的連接如何能夠更準確些。
例如,我們需要監(jiān)控一個連接的寫緩沖區(qū)是否空閑,滿足“可寫”時我們就可以從用戶態(tài)將響應調(diào)用write發(fā)送給客戶端 。但是,或者連接可寫時,我們的“響應”內(nèi)容還在磁盤上呢,此時若是磁盤讀取還未完成呢?肯定不能使線程阻塞的,那么就不發(fā)送響應了。但是,下一次epoll_wait時可能又把這個連接返回給你了,你還得檢查下是否要處理??赡埽覀兊某绦蛴辛硪粋€模塊專門處理磁盤IO,它會在磁盤IO完成時再發(fā)送響應。那么,每次epoll_wait都返回這個“可寫”的、卻無法立刻處理的連接,是否符合用戶預期呢?
于是,ET和LT模式就應運而生了。LT是每次滿足期待狀態(tài)的連接,都得在epoll_wait中返回,所以它一視同仁,都在一條水平線上。ET則不然,它傾向更精確的返回連接。在上面的例子中,連接第一次變?yōu)榭蓪懞?,若是程序未向連接上寫入任何數(shù)據(jù),那么下一次epoll_wait是不會返回這個連接的。ET叫做 邊緣觸發(fā),就是指,只有連接從一個狀態(tài)轉(zhuǎn)到另一個狀態(tài)時,才會觸發(fā)epoll_wait返回它??梢?,ET的編程要復雜不少,至少應用程序要小心的防止epoll_wait的返回的連接出現(xiàn):可寫時未寫數(shù)據(jù)后卻期待下一次“可寫”、可讀時未讀盡數(shù)據(jù)卻期待下一次“可讀”。
當然,從一般應用場景上它們性能是不會有什么大的差距的,ET可能的優(yōu)點是,epoll_wait的調(diào)用次數(shù)會減少一些,某些場景下連接在不必要喚醒時不會被喚醒(此喚醒指epoll_wait返回)。但如果像我上面舉例所說的,有時它不單純是一個網(wǎng)絡問題,跟應用場景相關(guān)。當然,大部分開源框架都是基于ET寫的,框架嘛,它追求的是純技術(shù)問題,當然力求盡善盡美
上述內(nèi)容就是一文帶你讀懂Java IO復用,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
網(wǎng)頁題目:一文帶你讀懂JavaIO復用-創(chuàng)新互聯(lián)
文章源于:http://jinyejixie.com/article16/hisdg.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣、網(wǎng)站改版、網(wǎng)站設計公司、網(wǎng)站營銷、App設計、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容