優(yōu)化雜談
Author :放翁
Blog:http://blog.csdn.net/cenwenchu79/
當(dāng)應(yīng)用遇到規(guī)模化問題的時候,就是考慮性能優(yōu)化的時候了。今天同事和我聊起了NIO在客戶端的使用與BIO有什么優(yōu)勢,也勾起了我前一陣子和其他同學(xué)交流優(yōu)化的一些想法,純粹個人的一點想法。
CPU利用率和Load
在過去做壓力測試的時候,我們經(jīng)常會關(guān)注兩個指標(biāo),CPU和Load。有同學(xué)覺得CPU利用率上去了Load肯定也上去了,Load上去了CPU利用率同樣會上去。但是在一些需要優(yōu)化的場景下,常常會看到Load很高,CPU利用率卻可能比較低(多核更是可能出現(xiàn)分配不均的情況)。Load其實就是等待處理的任務(wù)隊列,當(dāng)你的應(yīng)用在等待同步消息返回處理的同時,CPU還是會將時間切片分配給這些線程,而真正需要CPU的線程,卻不得不在到了時間片以后暫時放棄工作被掛起。因此在程序設(shè)計的時候就要考慮如何利用好CPU的這個資源,如何均勻的將壓力分?jǐn)偟礁鱾€CPU上(有時候就一個線程在不斷循環(huán),導(dǎo)致單個CPU負(fù)荷很高)。
NIO在客戶端的使用
Http消息設(shè)置keepalive和采用NIO的方式復(fù)用信道、BIO結(jié)合連接池的方式,最基本的目的就是降低建立TCP產(chǎn)生握手的成本,最大限度的復(fù)用已有的資源,但是否NIO就只有復(fù)用信道這點呢?
NIO和BIO在數(shù)據(jù)傳輸和處理的模式上有不同,NIO采用的是BufferPacket+Channel的模式,這其實和操作系統(tǒng)本身的傳輸模式很類似,而BIO的Stream的模式是Java自己獨特的模式。在采用NIO的這種數(shù)據(jù)傳輸模式以后,可以充分利用操作系統(tǒng)本身對傳輸?shù)膬?yōu)化,因此這是一方面好處。另一方面異步和事件機(jī)制的使用,可以降低對于昂貴的資源申請,在高并發(fā)下提高處理能力。
NIO客戶端的編程模型最大特點:依賴反置,松耦合帶來性能提升。在請求流程協(xié)議中支持“票根”,也就是我們說的回執(zhí)。例如,你今天面試完了,不需要你在阿里巴巴前臺等著結(jié)果,直接留個電話,有消息就會直接通知,電話就是通知結(jié)果和服務(wù)請求者的關(guān)聯(lián)手段。(此時阿里巴巴前臺和會議室就會有足夠的空間給其他人來面試,這就是資源)
服務(wù)端使用NIO就不多說了,這里主要說一下在客戶端的使用場景。兩者是否真的有很大的差別,是否NIO有絕對的優(yōu)勢,其實還是和場景有關(guān)。簡單說來就一個判斷標(biāo)準(zhǔn):應(yīng)用對于通道的利用率是否夠高。下面列了4種場景:
1. 一次請求數(shù)據(jù)量很少,服務(wù)處理速度很快。
2. 一次請求數(shù)據(jù)量很多,服務(wù)處理速度很快。
3. 一次請求數(shù)據(jù)量很少,服務(wù)處理速度很慢。
4. 一次請求數(shù)據(jù)量很多,服務(wù)處理速度很慢。
場景1,傳輸效率很高,服務(wù)處理速度很快,一次請求很快就被完成,采用NIO和BIO,在性能優(yōu)勢上除了操作系統(tǒng)對NIO的優(yōu)化以外,BIO連接池不輸于NIO。在易用性上,BIO更加容易處理。(NIO的異步機(jī)制,就要求消息傳輸協(xié)議需要有會話碼來提供異步處理入口選擇如何處理)
場景2,傳輸過程比較長,消耗時間比較多,服務(wù)處理速度很快,因此交互的時間大部分都還是在數(shù)據(jù)通道傳輸上,由于NIO在傳輸過程中依然是串行化的,因此BIO的連接池優(yōu)于NIO,同時NIO一個客戶端只有一個通道,因此BIO開的連接池越大,并行處理能力越強(qiáng),因此BIO效率比較好一些。
場景3,傳輸量比較少,服務(wù)處理比較慢,很明顯這是通道利用率低的表現(xiàn),NIO有絕對的優(yōu)勢,特別是在高并發(fā)下。信道和服務(wù)端客戶端資源被充分利用。
場景4,傳輸量比較多,服務(wù)處理也比較慢,這時候可以發(fā)現(xiàn)信道利用率取決于服務(wù)事件和傳輸消耗時間的比例,這類場景某些情況下BIO也會優(yōu)于NIO。
單線程和多線程
在使用多線程來優(yōu)化程序的時候,是否考慮過多線程的使用場景,多線程不是萬能藥,在某些情況下還可能是毒藥。使用多線程的過程中,需要考慮這么幾個因素:
1. 資源競爭,復(fù)雜度增加。
為什么前面提到的NIO客戶端在處理數(shù)據(jù)流發(fā)送和讀取的時候都是采用單線程,數(shù)據(jù)流的發(fā)送和讀取都是在一個數(shù)據(jù)通道上的,而讀取和發(fā)送本身時間消耗是固定的(不論是多線程還是單線程),同時增加了復(fù)雜度(需要處理數(shù)據(jù)包整合問題)。這其實就是在資源上的串行化操作直接導(dǎo)致了任務(wù)的串行化,因此任務(wù)多線程反而起到了反作用。
2. 是否是關(guān)鍵路徑的工作,占關(guān)鍵路徑的比例。
首先,在優(yōu)化以前需要考慮優(yōu)化的內(nèi)容是否是關(guān)鍵路徑的工作,如果不是,那么增加復(fù)雜度實現(xiàn)的多線程模式,就沒有價值。其次就是看是否是在關(guān)鍵路徑中占有比較大的比例,同樣的,還是投入產(chǎn)出比例(多線程帶來的復(fù)雜度以及在高并發(fā)下的一些資源保護(hù)措施都需要很多的維護(hù)成本)。
3. 任務(wù)的合理切分。
在NIO的客戶端,接受數(shù)據(jù)的事件將會寫得很輕量級,但是接受到數(shù)據(jù)然后分析數(shù)據(jù)還原成業(yè)務(wù)對象,則會通過線程池的方式來分別處理。就好比監(jiān)聽連接到來,和實際的去建立連接分成了兩個階段的任務(wù),讓事件型的任務(wù)單純,快速執(zhí)行,讓與業(yè)務(wù)相關(guān)的部分通過多線程并行的方式提高處理效率。總的來說就是把任務(wù)劃分成為系統(tǒng)性的任務(wù)和業(yè)務(wù)性的任務(wù),前者消耗時間少,設(shè)計盡量簡單高效,采用單線程處理即可,后者通常情況下在處理流程和資源上不沖突的情況可以通過多線程并行提高效率。
優(yōu)化應(yīng)用關(guān)注點:
A.關(guān)鍵路徑是否可以優(yōu)化,關(guān)鍵路徑的任務(wù)拆分。
B.關(guān)鍵路徑上的單個任務(wù)是否可以拆分并行執(zhí)行。(是否有資源競爭,是否會有流程上的前后依賴,是否增加復(fù)雜度引入新的不穩(wěn)定因素)
C.系統(tǒng)資源和依賴外部系統(tǒng)是否會成為瓶頸。(單機(jī)的CPU,IO都會在一定的壓力下成下降趨勢,并行執(zhí)行反而降低了處理能力)
因此,可以看到不論是MapReduce設(shè)計下的Hadoop,還是Erlang語言級別的特性,都盡量的希望任務(wù)之間可以并行執(zhí)行,相互之間低耦合,通過異步事件消息通知方式來交互,同時數(shù)據(jù)沒有共享,防止資源競爭導(dǎo)致無法并行高效處理。系統(tǒng)設(shè)計還是要根據(jù)場景來判斷使用什么方式優(yōu)化,越簡單越好。