自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

IO設(shè)計(jì):如何設(shè)計(jì)IO交互來提升系統(tǒng)性能?

開發(fā) 前端
在 Node.js 中引入了 async 和 await 機(jī)制(在 C++、Rust 中,也都引入了類似的機(jī)制),較好地解決了這個(gè)問題。我們使用這個(gè)機(jī)制,可以將背后的回調(diào)函數(shù)機(jī)制封裝到語言內(nèi)部的底層實(shí)現(xiàn)當(dāng)中,這樣我們依然能夠使用串行思維模式來處理 IO 交互。

對(duì)于一個(gè)軟件系統(tǒng)而言,其性能受諸多因素影響,其中與 IO 之間的交互是極為關(guān)鍵的一項(xiàng)。

然而,可能不少程序員認(rèn)為,IO 交互屬于操作系統(tǒng)底層的工作,似乎和上層業(yè)務(wù)關(guān)聯(lián)不大,因而較少關(guān)注 IO 交互的設(shè)計(jì)。實(shí)際上,這種認(rèn)識(shí)是不太科學(xué)的。

要知道,在對(duì)軟件性能進(jìn)行測(cè)試時(shí),倘若出現(xiàn)這樣一種奇特的現(xiàn)象:盡管 CPU 使用率尚未達(dá)到 100%,但系統(tǒng)吞吐量卻無法繼續(xù)提高。那么在這個(gè)時(shí)候,極有可能是由于 IO 交互設(shè)計(jì)不佳,致使軟件的眾多業(yè)務(wù)處理線程被阻塞,所以性能難以提升。

由此可見,在軟件設(shè)計(jì)過程中,良好的 IO 交互設(shè)計(jì)對(duì)于系統(tǒng)性能的提升有著至關(guān)重要的作用。

我首先要為您開拓一下思維,讓您知曉在軟件設(shè)計(jì)中可能會(huì)遭遇的各類 IO 場(chǎng)景,進(jìn)而樹立起關(guān)于 IO 交互設(shè)計(jì)的正確認(rèn)識(shí)。

接著,我會(huì)為您講解針對(duì)不同的 IO 場(chǎng)景,應(yīng)當(dāng)如何開展 IO 交互設(shè)計(jì),以便在軟件實(shí)現(xiàn)的復(fù)雜度和性能之間達(dá)成平衡,從而助力您增強(qiáng)在 IO 交互設(shè)計(jì)方面的能力。

那么接下來,就讓我們一同來了解一下,在軟件設(shè)計(jì)里究竟存在哪些 IO 場(chǎng)景吧。

突破對(duì) IO 的片面認(rèn)識(shí)

提到 IO,您首先想到的會(huì)是什么?是鍵盤、鼠標(biāo)、打印機(jī)嗎?實(shí)際上,在如今的軟件系統(tǒng)中,這些東西已經(jīng)很少被使用了。

一般來講,大多數(shù)程序員所理解的 IO 交互,是文件讀取操作、底層網(wǎng)絡(luò)通信等等。那么在這里,我想問您一個(gè)問題:是不是系統(tǒng)中沒有這些操作,就無需進(jìn)行 IO 交互設(shè)計(jì)了?答案其實(shí)是否定的。

對(duì)于一個(gè)軟件系統(tǒng)來說,除了 CPU 和內(nèi)存,對(duì)其他資源或者服務(wù)的訪問也能被看作是 IO 交互。例如針對(duì)數(shù)據(jù)庫的訪問、REST 請(qǐng)求,以及消息隊(duì)列的使用,都可以視作 IO 交互問題,因?yàn)檫@些軟件服務(wù)都位于不同的服務(wù)器上,直接的信息交互也是通過底層的 IO 設(shè)備來實(shí)現(xiàn)的。

接下來,我將帶您來看一段使用 Java 語言訪問 MongoDB 的代碼實(shí)現(xiàn),您會(huì)發(fā)現(xiàn),在軟件開發(fā)中,有許多與 IO 相關(guān)的代碼實(shí)現(xiàn)是比較隱蔽的,不容易被察覺。所以,您應(yīng)當(dāng)對(duì)這些 IO 相關(guān)的問題始終保持警惕,不能讓它們影響軟件的業(yè)務(wù)性能。

這段代碼的業(yè)務(wù)邏輯是在數(shù)據(jù)庫中查詢一條數(shù)據(jù)并返回,具體代碼如下:

// 從數(shù)據(jù)庫查詢一條數(shù)據(jù)。
MongoClient client = new MongoClient("*.*.*.*");
DBCollection collection = mClient.getDB("testDB").getCollection("firstCollection");
BasicDBObject queryObject = new BasicDBObject("name","999");
DBObject obj = collection.findOne(queryObject);  // 查詢操作

其中我們能夠發(fā)現(xiàn),代碼中的最后一行采用了同步阻塞的交互方式。這意味著,在這段代碼的執(zhí)行過程中,會(huì)將當(dāng)前線程阻塞住,此過程和讀取一個(gè)文件的代碼原理相同。所以,這也是一類十分典型的 IO 業(yè)務(wù)問題。由此可見,我們務(wù)必要突破對(duì)于傳統(tǒng) IO 的那種狹隘理解與認(rèn)識(shí),運(yùn)用更具全局性、系統(tǒng)性的視角,去認(rèn)識(shí)系統(tǒng)里的各類 IO 場(chǎng)景,這是做好基于 IO 交互設(shè)計(jì),提升軟件性能的前提條件。

那么說到此,我們具體應(yīng)該怎樣針對(duì)系統(tǒng)中不同的 IO 場(chǎng)景,進(jìn)行交互設(shè)計(jì)并提升系統(tǒng)性能呢?接下來,我就為您詳細(xì)介紹在軟件設(shè)計(jì)中,IO 交互設(shè)計(jì)的不同實(shí)現(xiàn)模式,從而幫助您理解不同的 IO 交互對(duì)軟件設(shè)計(jì)與實(shí)現(xiàn)以及在性能方面的影響

IO 交互設(shè)計(jì)與軟件設(shè)計(jì)

我們了解到,在 Linux 操作系統(tǒng)內(nèi)核里,內(nèi)置了 5 種各異的 IO 交互模式,即阻塞 IO、非阻塞 IO、多路復(fù)用 IO、信號(hào)驅(qū)動(dòng) IO、異步 IO。然而,不同的編程語言和代碼庫,均基于底層 IO 接口重新封裝了一層接口,并且這些接口在使用方面存在諸多差異。

正因如此,致使許多程序員對(duì) IO 交互模型的理解和認(rèn)識(shí)無法統(tǒng)一,進(jìn)而為做好 IO 的交互設(shè)計(jì)與實(shí)現(xiàn)帶來了較大的阻礙。所以接下來,我將會(huì)從業(yè)務(wù)使用的視角出發(fā),把 IO 交互設(shè)計(jì)劃分為三種方式,分別是同步阻塞交互方式、同步非阻塞交互方式和異步回調(diào)交互方式,并為您逐一介紹它們的設(shè)計(jì)原理。

我覺得,只要您弄清楚這些 IO 交互設(shè)計(jì)的原理,并且理解它們?cè)诓煌?IO 場(chǎng)景中,怎樣在軟件實(shí)現(xiàn)復(fù)雜度與性能之間做好權(quán)衡,您距離設(shè)計(jì)出高性能的軟件就不遠(yuǎn)了。

另外在此您需要知曉,這三種交互設(shè)計(jì)方式存在層層遞進(jìn)的關(guān)系,越是靠后的方式,在 IO 交互過程中,CPU 介入的開銷可能就越少。當(dāng)然,CPU 介入越少,也就意味著在相同的 CPU 硬件資源上,潛在能夠支撐更多的業(yè)務(wù)處理流程,從而性能就有可能會(huì)更高。

同步阻塞交互方式

首先,讓我們來瞧瞧第一種 IO 交互方式:同步阻塞交互方式。那什么叫做同步阻塞交互方式呢?在 Java 語言里,傳統(tǒng)基于流的讀寫操作方式,實(shí)際上運(yùn)用的就是同步阻塞方式,前面我所介紹的那個(gè) MongoDB 的查詢請(qǐng)求,同樣是同步阻塞的交互方式。也就是說,盡管從開發(fā)人員的角度來看,采用同步阻塞交互方式的程序?qū)儆谕秸{(diào)用,然而在實(shí)際的執(zhí)行進(jìn)程中,程序會(huì)被操作系統(tǒng)掛起并阻塞。接下來,我們看看采用了同步阻塞交互方式的原理示意圖:

圖片圖片

從圖中您能夠看到,在業(yè)務(wù)代碼發(fā)出讀寫請(qǐng)求之后,當(dāng)前的線程或進(jìn)程會(huì)被阻塞,只有等到 IO 處理完畢才會(huì)被喚醒。

所以在這里您或許會(huì)產(chǎn)生一個(gè)疑問:使用同步阻塞交互方式,性能是不是就一定會(huì)很差呢?實(shí)際上,并非如此絕對(duì),因?yàn)椴⒎撬械?IO 訪問場(chǎng)景都屬于性能關(guān)鍵的場(chǎng)景。

我給您舉個(gè)例子,就比如在程序啟動(dòng)過程中加載配置文件的場(chǎng)景,由于軟件在運(yùn)行過程中只會(huì)加載配置文件一次,所以這次的讀取操作并不會(huì)對(duì)軟件的業(yè)務(wù)性能造成影響,如此一來,我們就應(yīng)當(dāng)選擇最為簡(jiǎn)單的實(shí)現(xiàn)方式,即同步阻塞交互方式。

既然這樣,您可能又要問了:倘若系統(tǒng)中存在很多此類 IO 請(qǐng)求操作,那么軟件系統(tǒng)架構(gòu)會(huì)是什么樣的呢?實(shí)際上,早期的 Java 服務(wù)器端經(jīng)常運(yùn)用 Socket 通信,采用的也是同步阻塞交互方式,其對(duì)應(yīng)的架構(gòu)圖是這樣的:

圖片圖片

可以看到,每個(gè) Socket 都會(huì)單獨(dú)使用一個(gè)線程,在使用 Socket 接口進(jìn)行寫入或讀取數(shù)據(jù)時(shí),這個(gè)對(duì)應(yīng)的線程就會(huì)被阻塞。

那么對(duì)于這樣的架構(gòu)而言,如果系統(tǒng)中的連接數(shù)較少,即便某一個(gè)線程出現(xiàn)了阻塞,還有其他的業(yè)務(wù)線程能夠正常處理請(qǐng)求,所以它的系統(tǒng)性能實(shí)際上并非很差。

不過,當(dāng)下很多基于 Java 開發(fā)的后端服務(wù),在訪問數(shù)據(jù)庫的時(shí)候?qū)嶋H上也是運(yùn)用同步阻塞的方式,所以就只能采用眾多的線程,分別去處理不同的數(shù)據(jù)庫操作請(qǐng)求。

而要是針對(duì)系統(tǒng)中線程數(shù)眾多的場(chǎng)景,每次訪問數(shù)據(jù)庫都會(huì)引發(fā)阻塞,那么就極易致使系統(tǒng)的性能受到限制。

由此,我們需要考慮采用其他類型的 IO 交互方式,避免因頻繁地進(jìn)行線程間切換而導(dǎo)致 CPU 資源浪費(fèi),從而進(jìn)一步提升軟件的性能。

所以,同步非阻塞交互模式被提出,其目的就是為了解決這個(gè)問題,下面我們具體來看看它的設(shè)計(jì)原理。

同步非阻塞交互方式

這里,我們先來了解下同步非阻塞交互方式的設(shè)計(jì)特點(diǎn):在請(qǐng)求 IO 交互的過程中,如果 IO 交互沒有結(jié)束的話,當(dāng)前線程或者進(jìn)程并不會(huì)被阻塞,而是會(huì)去執(zhí)行其他的業(yè)務(wù)代碼,然后等過段時(shí)間再來查詢 IO 交互是否完成。Java 語言在 1.4 版本之后引入的 NIO 交互模式,其實(shí)就屬于同步非阻塞的模式。

那么接下來,我們就通過一個(gè) SocketChannel 在非阻塞模式中讀取數(shù)據(jù)的代碼片段,來具體看看同步非阻塞交互方式的工作原理:

while(selector.select()>0){   //不斷循環(huán)選擇可操作的通道。
for(SelectionKey sk:selector.selectedKeys()){
        selector.selectedKeys().remove(sk);
if(sk.isReadable()){ //是一個(gè)可讀的通道
            SocketChannel sc=(SocketChannel)sk.channel();
            String cnotallow="";
            ByteBuffer buff=ByteBuffer.allocate(1024);
while(sc.read(buff)>0){
                sc.read(buff);
                buff.flip();
                content+=charset.decode(bff);
                }
            System.out.println(content);
            sk.interestOps(SelectionKey.OP_READ);          
        }
    }
}

您能看到,業(yè)務(wù)代碼中會(huì)持續(xù)不斷地循環(huán)執(zhí)行 selector.select() 操作,挑選出可讀就緒的 SocketChannel,而后再調(diào)用 channel.read,將通道數(shù)據(jù)讀取到 Buffer 中。也就是說,在這段代碼的執(zhí)行過程里,SocketChannel 從網(wǎng)口設(shè)備接收數(shù)據(jù)的期間,不會(huì)長時(shí)間地阻塞當(dāng)前業(yè)務(wù)線程的執(zhí)行,所以能夠進(jìn)一步提升性能。這個(gè) IO 交互方式對(duì)應(yīng)的原理圖如下:

圖片圖片

從圖中您能看到,當(dāng)前的業(yè)務(wù)線程雖然避免了長時(shí)間被阻塞掛起,然而在業(yè)務(wù)線程里,會(huì)頻繁地調(diào)用 selector.select 接口來查詢狀態(tài)。這意味著,在單 IO 通道的場(chǎng)景下,運(yùn)用這種同步非阻塞交互方式,性能的提升實(shí)際上是非常有限的。

不過,與同步阻塞交互方式恰好相反,當(dāng)業(yè)務(wù)系統(tǒng)中同時(shí)存在眾多的 IO 交互通道時(shí),使用同步非阻塞交互方式,我們能夠復(fù)用一個(gè)線程,來查詢可讀就緒的通道,如此便能夠大大降低由 IO 交互引起的頻繁切換線程的開銷。

因此,在軟件設(shè)計(jì)的過程中,如果您發(fā)現(xiàn)核心業(yè)務(wù)邏輯也存在多 IO 交互的問題,您就能夠基于這種 IO 同步非阻塞交互方式,來支撐產(chǎn)品的軟件架構(gòu)設(shè)計(jì)。在采用這種 IO 交互設(shè)計(jì)方式來實(shí)現(xiàn)多個(gè) IO 交互時(shí),它的軟件架構(gòu)如下圖所示:

圖片圖片

如果您仔細(xì)閱讀了前面 SocketChannel 在非阻塞模式中讀取數(shù)據(jù)的代碼片段,就會(huì)發(fā)現(xiàn)這個(gè)圖中包含了三個(gè)頗為熟悉的概念,分別是 Buffer、Channel、Selector,它們正是 Java NIO 的核心。在此我也為您簡(jiǎn)單介紹一下:Buffer 是一個(gè)用于緩存讀取和寫入數(shù)據(jù)的緩沖區(qū);Channel 是一個(gè)負(fù)責(zé)在后臺(tái)對(duì)接 IO 數(shù)據(jù)的通道;而 Selector 所實(shí)現(xiàn)的主要功能,便是主動(dòng)查詢哪些通道處于就緒狀態(tài)。所以,Java NIO 正是基于這個(gè) IO 交互模型,支撐業(yè)務(wù)代碼實(shí)現(xiàn)針對(duì) IO 的同步非阻塞設(shè)計(jì),進(jìn)而降低了原來傳統(tǒng)的同步阻塞 IO 交互過程中,線程頻繁被阻塞和切換的開銷。

不過,基于同步非阻塞方式的 IO 交互設(shè)計(jì),倘若在并發(fā)設(shè)計(jì)中,未能平衡好 IO 狀態(tài)查詢與業(yè)務(wù)處理 CPU 執(zhí)行開銷的管理,就極易致使軟件執(zhí)行期間存在大量的 IO 狀態(tài)冗余查詢,進(jìn)而造成對(duì) CPU 資源的浪費(fèi)。

因此,我們?nèi)孕鑿臉I(yè)務(wù)角度的 IO 交互設(shè)計(jì)著手,進(jìn)一步降低 IO 給 CPU 帶來的額外開銷,而這正是接下來我要為您介紹的異步回調(diào)交互方式的重要優(yōu)勢(shì)。

異步回調(diào)交互方式

所謂異步回調(diào),其含義為,當(dāng)業(yè)務(wù)代碼觸發(fā) IO 接口調(diào)用之后,當(dāng)前的線程會(huì)繼續(xù)執(zhí)行后續(xù)的處理流程,而后等到 IO 處理結(jié)束,再通過回調(diào)函數(shù)來執(zhí)行 IO 結(jié)束后的代碼邏輯。

這里我們同樣來看一段代碼示例,這是 Java 語言針對(duì) MongoDB 的插入操作,其采用的便是異步回調(diào)的實(shí)現(xiàn)方式:

Document doc = new Document("name", "Geek")
               .append("info", new Document("age", 203).append("sex", "male"));


collection.insertOne(doc, new SingleResultCallback<Void>() { 
@Override
public void onResult(final Void result, final Throwable t) {
        System.out.println("Inserted success");
    }
});


我們能夠發(fā)現(xiàn),在這段代碼里,調(diào)用 collection.insertOne 進(jìn)行插入數(shù)據(jù)時(shí),同時(shí)傳入了回調(diào)函數(shù)。實(shí)際上,這個(gè) MongoDB 訪問接口在底層運(yùn)用的是 Netty 框架,只是重新封裝了接口的使用方法罷了。由此,我們能夠最大程度地降低 IO 交互過程中 CPU 參與的開銷。這種 IO 交互方式的原理圖如下所示:

圖片圖片

從這個(gè)圖中能夠看到,在運(yùn)用異步回調(diào)這種處理方式時(shí),回調(diào)函數(shù)常常會(huì)被掛載到另外一個(gè)線程中去執(zhí)行。所以采用這種方式存在一個(gè)好處,即業(yè)務(wù)邏輯無需頻繁地查詢數(shù)據(jù),但與此同時(shí),它也會(huì)引入一個(gè)新的問題,那便是回調(diào)處理函數(shù)與正常的業(yè)務(wù)代碼被割裂開來,這會(huì)給代碼的實(shí)現(xiàn)增添許多的復(fù)雜度。

我給您舉個(gè)例子,如果代碼中的回調(diào)函數(shù)在處理過程中,還需要進(jìn)一步執(zhí)行其他 IO 請(qǐng)求,倘若再使用回調(diào)機(jī)制,那么就會(huì)出現(xiàn)可惡的回調(diào)嵌套問題,也就是回調(diào)函數(shù)中再嵌套一個(gè)回調(diào)函數(shù),如此一直嵌套下去,代碼就會(huì)變得難以閱讀和維護(hù)。

所以后來,在 Node.js 中引入了 async 和 await 機(jī)制(在 C++、Rust 中,也都引入了類似的機(jī)制),較好地解決了這個(gè)問題。我們使用這個(gè)機(jī)制,可以將背后的回調(diào)函數(shù)機(jī)制封裝到語言內(nèi)部的底層實(shí)現(xiàn)當(dāng)中,這樣我們依然能夠使用串行思維模式來處理 IO 交互。

而且,當(dāng)具備這種機(jī)制之后,IO 交互方式對(duì)軟件設(shè)計(jì)架構(gòu)的影響就相對(duì)較少了,所以像 Node.js 這樣的單進(jìn)程模型也能夠處理數(shù)量眾多的 IO 請(qǐng)求。

另外,使用異步回調(diào)交互方式還有一個(gè)好處,因?yàn)樵诋?dāng)下的互聯(lián)網(wǎng)場(chǎng)景中,對(duì)于數(shù)據(jù)庫、消息隊(duì)列、REST 請(qǐng)求的使用是非常頻繁的,所以如果您采用異步回調(diào)方式,就比較有可能將 IO 阻塞引發(fā)的線程切換開銷,以及頻繁查詢 IO 狀態(tài)的時(shí)間開銷,都降低到一個(gè)較低的狀態(tài)。

最后我還想要告訴您的是,實(shí)際上,IO 交互設(shè)計(jì)不僅與語言系統(tǒng)的并發(fā)設(shè)計(jì)有著很大的關(guān)聯(lián),而且與緩沖區(qū)(Buffer)的設(shè)計(jì)和實(shí)現(xiàn)關(guān)系也十分緊密,我們?cè)谶M(jìn)行 IO 交互設(shè)計(jì)時(shí),實(shí)際上需要權(quán)衡眾多因素,這是一項(xiàng)頗為復(fù)雜的工作,我們絕對(duì)不能輕視它。

責(zé)任編輯:武曉燕 來源: 二進(jìn)制跳動(dòng)
相關(guān)推薦

2024-11-08 14:27:52

系統(tǒng)設(shè)計(jì)數(shù)據(jù)庫

2016-09-26 13:50:52

Linux系統(tǒng)性能

2019-03-04 10:45:57

Linux Cockp系統(tǒng)性能命令

2009-02-18 20:27:24

組策略提升Windows性能

2015-07-28 09:19:10

Linux內(nèi)核

2011-08-09 17:15:45

注冊(cè)表注冊(cè)表編輯器

2016-10-27 08:39:35

大數(shù)據(jù)設(shè)計(jì)定量

2024-12-11 07:59:02

2018-12-10 15:13:06

緩存系統(tǒng)性能數(shù)據(jù)

2009-03-22 19:19:15

多核多核服務(wù)器多核歷史

2018-09-10 10:20:26

磁盤 IO網(wǎng)絡(luò) IO監(jiān)控

2023-02-27 07:22:53

RPC網(wǎng)絡(luò)IO

2011-06-02 10:07:28

iostatlinux

2015-11-10 16:55:00

性能IO子系統(tǒng)Linux

2023-06-12 00:22:50

操作系統(tǒng)應(yīng)用程序內(nèi)核鎖

2023-10-26 08:33:16

Redis管道技術(shù)

2013-08-15 14:10:24

云主機(jī)磁盤IO

2017-07-07 16:36:28

BIOIO模型 NIO

2023-10-23 08:23:16

系統(tǒng)性能數(shù)據(jù)庫

2023-10-17 14:35:22

人工智能AI
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)