系统间通信4:基本IO通信模型
本文引用 : https://blog.csdn.net/yinwenjie/article/details/48472237
目前常用的IO通信模型包括四種:阻塞式同步IO、非阻塞式同步IO、多路復(fù)用IO和真正的異步IO。所有IO模式都是要靠操作系統(tǒng)進(jìn)行支持,應(yīng)用程序只是提供相應(yīng)的實(shí)現(xiàn),對(duì)操作系統(tǒng)進(jìn)行調(diào)用。
1. 傳統(tǒng)阻塞模式(BIO)
BIO就是:blocking IO。最容易理解、最容易實(shí)現(xiàn)的IO工作方式,**應(yīng)用程序向操作系統(tǒng)請(qǐng)求網(wǎng)絡(luò)IO操作,這時(shí)應(yīng)用程序會(huì)一直等待;另一方面,操作系統(tǒng)收到請(qǐng)求后,也會(huì)等待,直到網(wǎng)絡(luò)上有數(shù)據(jù)傳到監(jiān)聽端口;操作系統(tǒng)在收集數(shù)據(jù)后,會(huì)把數(shù)據(jù)發(fā)送給應(yīng)用程序;最后應(yīng)用程序受到數(shù)據(jù),并解除等待狀態(tài)。**如下圖所示:
注意:上圖中交互的兩個(gè)元素是應(yīng)用程序和它所使用的操作系統(tǒng)
就TCP協(xié)議來說,整個(gè)過程實(shí)際上分成三個(gè)步驟:三次握手建立連接、傳輸數(shù)據(jù)(包括驗(yàn)證和重發(fā))、斷開連接。
BIO存在的問題:
- 同一時(shí)間,服務(wù)器只能接受來自于客戶端A的請(qǐng)求信息;雖然客戶端A和客戶端B的請(qǐng)求是同時(shí)進(jìn)行的,但客戶端B發(fā)送的請(qǐng)求信息只能等到服務(wù)器接受完A的請(qǐng)求數(shù)據(jù)后,才能被接受。
- 由于服務(wù)器一次只能處理一個(gè)客戶端請(qǐng)求,當(dāng)處理完成并返回后(或者異常時(shí)),才能進(jìn)行第二次請(qǐng)求的處理。很顯然,這樣的處理方式在高并發(fā)的情況下,是不能采用的。
- 實(shí)際上以上的問題是可以通過多線程來解決的,實(shí)際上就是當(dāng)accept接收到一個(gè)客戶端的連接后,服務(wù)器端啟動(dòng)一個(gè)新的線程,來讀寫客戶端的數(shù)據(jù),并完成相應(yīng)的業(yè)務(wù)處理。但是你無法影響操作系統(tǒng)底層的“同步IO”機(jī)制。
2. 非阻塞模式(NIO)
一定要注意:阻塞/非阻塞的描述是針對(duì)應(yīng)用程序中的線程進(jìn)行的,對(duì)于阻塞方式的一種改進(jìn)是應(yīng)用程序?qū)⑵洹耙恢钡却钡臓顟B(tài)主動(dòng)打開,如下圖所示:
這種模式下,應(yīng)用程序的線程不再一直等待操作系統(tǒng)的IO狀態(tài),而是在等待一段時(shí)間后,就解除阻塞。
引入了多線程技術(shù)后,IO的處理吞吐量大大提高了,但是這樣做就真的沒有問題了嗎,您要知道操作系統(tǒng)可是有“最大線程”限制的:
- 雖然在服務(wù)器端,請(qǐng)求的處理交給了一個(gè)獨(dú)立線程進(jìn)行,但是操作系統(tǒng)通知accept()的方式還是單個(gè)處理的(甚至都不是非阻塞模式)。也就是說,實(shí)際上是服務(wù)器接收到數(shù)據(jù)報(bào)文后的“業(yè)務(wù)處理過程”可以多線程(包括可以是非阻塞模式),但是數(shù)據(jù)報(bào)文的接受還是需要一個(gè)一個(gè)的來。
- 在linux系統(tǒng)中,可以創(chuàng)建的線程是有限的。我們可以通過cat /proc/sys/kernel/threads-max 命令查看可以創(chuàng)建的最大線程數(shù)。當(dāng)然這個(gè)值是可以更改的,但是線程越多,CPU切換所需的時(shí)間也就越長,用來處理真正業(yè)務(wù)的需求也就越少。
- 創(chuàng)建一個(gè)線程是有較大的資源消耗的。JVM創(chuàng)建一個(gè)線程的時(shí)候,即使這個(gè)線程不做任何的工作,JVM都會(huì)分配一個(gè)堆棧空間。這個(gè)空間的大小默認(rèn)為128K,您可以通過-Xss參數(shù)進(jìn)行調(diào)整。
- 可以使用ThreadPoolExecutor線程池來緩解線程的創(chuàng)建問題,但是又會(huì)造成BlockingQueue積壓任務(wù)的持續(xù)增加,同樣消耗了大量資源。另外,如果您的應(yīng)用程序大量使用長連接的話,線程是不會(huì)關(guān)閉的。這樣系統(tǒng)資源的消耗更容易失控。
最后,無論您是使用的多線程、還是加入了非阻塞模式,這都是在應(yīng)用程序?qū)用娴奶幚?#xff0c;而底層socketServer所匹配的操作系統(tǒng)的IO模型始終是“同步IO”,最根本的問題并沒有解決。
那么,如果你真想單純使用線程來解決問題,那么您自己都可以計(jì)算出來您一個(gè)服務(wù)器節(jié)點(diǎn)可以一次接受多大的并發(fā)了。看來,單純使用線程解決這個(gè)問題不是最好的辦法。
3. 多路復(fù)用IO(IO Multiplex)
目前流程的多路復(fù)用IO實(shí)現(xiàn)主要包括四種:select、poll、epoll、kqueue。下表是他們的一些重要特性的比較:
多路復(fù)用IO技術(shù)最適用的是“高并發(fā)”場景,所謂高并發(fā)是指1毫秒內(nèi)至少同時(shí)有上千個(gè)連接請(qǐng)求準(zhǔn)備好 QPS~100W。其他情況下多路復(fù)用IO技術(shù)發(fā)揮不出來它的優(yōu)勢(shì)。
3.1 重要概念:Channel
通道,被建立的一個(gè)應(yīng)用程序和操作系統(tǒng)交互事件、傳遞內(nèi)容的渠道(注意是連接到操作系統(tǒng))。一個(gè)通道會(huì)有一個(gè)專屬的文件狀態(tài)描述符。那么既然是和操作系統(tǒng)進(jìn)行內(nèi)容的傳遞,那么說明應(yīng)用程序可以通過通道讀取數(shù)據(jù),也可以通過通道向操作系統(tǒng)寫數(shù)據(jù)。
3.2 重要概念:Buffer
數(shù)據(jù)緩存區(qū):在JAVA NIO 框架中,為了保證每個(gè)通道的數(shù)據(jù)讀寫速度JAVA NIO 框架為每一種需要支持?jǐn)?shù)據(jù)讀寫的通道集成了Buffer的支持。
這句話怎么理解呢?例如ServerSocketChannel通道它只支持對(duì)OP_ACCEPT事件的監(jiān)聽,所以它是不能直接進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)內(nèi)容的讀寫的。所以ServerSocketChannel是沒有集成Buffer的。
Buffer有兩種工作模式:寫模式和讀模式。在讀模式下,應(yīng)用程序只能從Buffer中讀取數(shù)據(jù),不能進(jìn)行寫操作。但是在寫模式下,應(yīng)用程序是可以進(jìn)行讀操作的,這就表示可能會(huì)出現(xiàn)臟讀的情況。所以一旦您決定要從Buffer中讀取數(shù)據(jù),一定要將Buffer的狀態(tài)改為讀模式。
3.3 重要概念:Selector
Selector的英文含義是“選擇器”,不過根據(jù)我們?cè)敿?xì)介紹的Selector的崗位職責(zé),您可以把它稱之為“輪詢代理器”、“事件訂閱器”、“channel容器管理機(jī)”都行。
- 事件訂閱和Channel管理:
應(yīng)用程序?qū)⑾騍elector對(duì)象注冊(cè)需要它關(guān)注的Channel,以及具體的某一個(gè)Channel會(huì)對(duì)哪些IO事件感興趣。Selector中也會(huì)維護(hù)一個(gè)“已經(jīng)注冊(cè)的Channel”的容器。 - 輪詢代理:
應(yīng)用層不再通過阻塞模式或者非阻塞模式直接詢問操作系統(tǒng)“事件有沒有發(fā)生”,而是由Selector代其詢問。 - 實(shí)現(xiàn)不同操作系統(tǒng)的支持:
之前已經(jīng)提到過,多路復(fù)用IO技術(shù) 是需要操作系統(tǒng)進(jìn)行支持的,其特點(diǎn)就是操作系統(tǒng)可以同時(shí)掃描同一個(gè)端口上不同網(wǎng)絡(luò)連接的時(shí)間。所以作為上層的JVM,必須要為不同操作系統(tǒng)的多路復(fù)用IO實(shí)現(xiàn)編寫不同的代碼。
通過上文的描述,我們知道了多路復(fù)用IO技術(shù)是操作系統(tǒng)的內(nèi)核實(shí)現(xiàn)。在不同的操作系統(tǒng),甚至同一系列操作系統(tǒng)的版本中所實(shí)現(xiàn)的多路復(fù)用IO技術(shù)都是不一樣的。那么作為跨平臺(tái)的JAVA JVM來說如何適應(yīng)多種多樣的多路復(fù)用IO技術(shù)實(shí)現(xiàn)呢?面向?qū)ο蟮耐惋@現(xiàn)出來了:無論使用哪種實(shí)現(xiàn)方式,他們都會(huì)有“選擇器”、“通道”、“緩存”這幾個(gè)操作要素,那么可以為不同的多路復(fù)用IO技術(shù)創(chuàng)建一個(gè)統(tǒng)一的抽象組,并且為不同的操作系統(tǒng)進(jìn)行具體的實(shí)現(xiàn)。JAVA NIO中對(duì)各種多路復(fù)用IO的支持,主要的基礎(chǔ)是java.nio.channels.spi.SelectorProvider抽象類,其中的幾個(gè)主要抽象方法包括:
-
public abstract DatagramChannel openDatagramChannel():創(chuàng)建和這個(gè)操作系統(tǒng)匹配的UDP 通道實(shí)現(xiàn)。
-
public abstract AbstractSelector openSelector():創(chuàng)建和這個(gè)操作系統(tǒng)匹配的NIO選擇器,就像上文所述,不同的操作系統(tǒng),不同的版本所默認(rèn)支持的NIO模型是不一樣的。
-
public abstract ServerSocketChannel openServerSocketChannel():創(chuàng)建和這個(gè)NIO模型匹配的服務(wù)器端通道。
-
public abstract SocketChannel openSocketChannel():創(chuàng)建和這個(gè)NIO模型匹配的TCP Socket套接字通道(用來反映客戶端的TCP連接)
多路復(fù)用IO的優(yōu)缺點(diǎn):
- 不用再使用多線程來進(jìn)行IO處理了(包括操作系統(tǒng)內(nèi)核IO管理模塊和應(yīng)用程序進(jìn)程而言)。當(dāng)然實(shí)際業(yè)務(wù)的處理中,應(yīng)用程序進(jìn)程還是可以引入線程池技術(shù)的
- 同一個(gè)端口可以處理多種協(xié)議,例如,使用ServerSocketChannel測(cè)測(cè)的服務(wù)器端口監(jiān)聽,既可以處理TCP協(xié)議又可以處理UDP協(xié)議。
- 操作系統(tǒng)級(jí)別的優(yōu)化:多路復(fù)用IO技術(shù)可以是操作系統(tǒng)級(jí)別在一個(gè)端口上能夠同時(shí)接受多個(gè)客戶端的IO事件。同時(shí)具有之前我們講到的阻塞式同步IO和非阻塞式同步IO的所有特點(diǎn)。Selector的一部分作用更相當(dāng)于“輪詢代理器”。
- 都是同步IO:目前我們介紹的 阻塞式IO、非阻塞式IO甚至包括多路復(fù)用IO,這些都是基于操作系統(tǒng)級(jí)別對(duì)“同步IO”的實(shí)現(xiàn)。
我們一直在說“同步IO”,一直都沒有詳細(xì)說,什么叫做“同步IO”。實(shí)際上一句話就可以說清楚:只有上層(包括上層的某種代理機(jī)制)系統(tǒng)詢問我是否有某個(gè)事件發(fā)生了,否則我不會(huì)主動(dòng)告訴上層系統(tǒng)事件發(fā)生了。很明顯,這里是可以繼續(xù)優(yōu)化的。
4. 異步IO(真正的NIO,AIO)
上述阻塞式同步IO、非阻塞式同步IO、多路復(fù)用IO 說明了IO模型是由操作系統(tǒng)提供支持,且這三種IO模型都是同步IO,都是采用的“應(yīng)用程序不詢問我,我絕不會(huì)主動(dòng)通知”的方式。
異步IO則是采用“訂閱-通知”模式:即應(yīng)用程序向操作系統(tǒng)注冊(cè)IO監(jiān)聽,然后繼續(xù)做自己的事情。當(dāng)操作系統(tǒng)發(fā)生IO事件,并且準(zhǔn)備好數(shù)據(jù)后,在主動(dòng)通知應(yīng)用程序,觸發(fā)相應(yīng)的函數(shù):
和同步IO一樣,異步IO也是由操作系統(tǒng)進(jìn)行支持的。微軟的windows系統(tǒng)提供了一種異步IO技術(shù):IOCP(I/O Completion Port,I/O完成端口);Linux下由于沒有這種異步IO技術(shù),可以使用的是epoll對(duì)異步IO進(jìn)行模擬。
總結(jié)
以上是生活随笔為你收集整理的系统间通信4:基本IO通信模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IM即时通讯服务将成联结谷歌、雅虎纽带(
- 下一篇: 机器学习资源-Harvard Ph.D