海量數(shù)據(jù)處理之系統(tǒng)過載保護(hù)
前段時間在網(wǎng)上看到騰訊后臺開發(fā)總監(jiān)bison分享的一篇文章《淺談過載保護(hù)》,讀來受益匪淺。剛好自己也在處理系統(tǒng)請求過載的問題,把自己的一些心得體會總結(jié)出來拿來與大家一起探討。
在bison的文章中談到:對于延時敏感的服務(wù),當(dāng)外部請求超過系統(tǒng)處理能力,如果系統(tǒng)沒有做相應(yīng)保護(hù),可能導(dǎo)致歷史累計(jì)的超時請求達(dá)到一定的規(guī)模,像雪球一樣形成惡性循環(huán),由于系統(tǒng)處理的每個請求都因?yàn)槌瑫r而無效,系統(tǒng)對外呈現(xiàn)的服務(wù)能力為0,且這種情況不能自動恢復(fù)。我們的系統(tǒng)就是要盡量避免這種情況的出現(xiàn),下面將詳細(xì)來分析一個現(xiàn)實(shí)中的案例。
一 有過載問題的系統(tǒng)
數(shù)據(jù)處理流程:
1) 前端將請求發(fā)送給數(shù)據(jù)解析及轉(zhuǎn)發(fā)系統(tǒng),
2)數(shù)據(jù)解析及轉(zhuǎn)發(fā)系統(tǒng)將封裝好的數(shù)據(jù)發(fā)送后臺數(shù)據(jù)請求,設(shè)置超時時間(假設(shè)300ms),線程同步等待處理結(jié)果從后臺返回。
3)在300ms內(nèi)正確返回結(jié)果后,則將處理的結(jié)果返回給前端,如果在300ms內(nèi)超時,則將數(shù)據(jù)發(fā)送到一次超時處理系統(tǒng)(假設(shè)設(shè)置超時時間500ms),線程同步等待結(jié)果返回。
4)在500ms內(nèi)正確返回結(jié)果后,則將處理的結(jié)果返回給前端,如果再一次超時,返回一個默認(rèn)的處理結(jié)果給前端,后端對數(shù)據(jù)進(jìn)行本地化,然后可以將數(shù)據(jù)發(fā)送到離線處理系統(tǒng)進(jìn)行二次處理。
數(shù)據(jù)解析的機(jī)器為多核,數(shù)據(jù)解析及轉(zhuǎn)發(fā)系統(tǒng)采用的是單進(jìn)程多線程模型,在前一篇文章《海量數(shù)據(jù)處理系列之Java線程池使用》詳細(xì)描述了多線程處理的實(shí)現(xiàn),采取的是無界隊(duì)列線程池的實(shí)現(xiàn),這樣從客戶端來的請求,會被這樣處理:
1) 如果線程池中有空閑線程,會將請求直接交給線程處理。
2) 如果沒有空閑線程,就將請求保存到任務(wù)隊(duì)列。
假設(shè)開50個線程,每個線程秒平均處理一個請求,那么系統(tǒng)每秒可以處理的最大請求數(shù)是50個。一旦前端數(shù)據(jù)請求超過50個每秒,在任務(wù)隊(duì)列中將會堆積大量的請求,前臺不斷發(fā)送過來,后來處理不過來,前端又設(shè)置了套接字超時,導(dǎo)致隊(duì)列中的大量請求超時,直接使得后端線程從隊(duì)列中取出套接字解析的時候,套接字已經(jīng)被前臺關(guān)閉了,引發(fā)I/O異常。堆積的量一旦雪崩,將使前臺發(fā)送過來的請求全部I/O異常,后臺處理系統(tǒng)跟掛掉無異了。
二 相對完善的系統(tǒng)
在上面的系統(tǒng)中,對請求是來者不拒的狀態(tài),具體來講就是將所有的請求都保存到任務(wù)隊(duì)列。請求堆積到一定程度,隊(duì)列中的很多請求都超時,這是可以采取清空請求隊(duì)列的方式,這個可以通過采取一定的監(jiān)控方式來實(shí)現(xiàn)。例如上圖中的心跳監(jiān)控模塊,它可以通過這樣的方式來實(shí)現(xiàn),就是模擬客戶端的請求,每隔一定時間發(fā)送一些請求過去,如果有大部分都正常返回,說明后端處理系統(tǒng)正常;當(dāng)出現(xiàn)大部分超時的時候,說明后臺系統(tǒng)已經(jīng)掛掉了,這時候重啟數(shù)據(jù)解析及轉(zhuǎn)發(fā)系統(tǒng),清空系統(tǒng)中的任務(wù)請求隊(duì)列,這樣可以暫時處理請求高峰期的情況。
但是這個方式也是治標(biāo)不治本的,后臺最多只能處理這么多請求,重啟后照樣會導(dǎo)致大量堵塞導(dǎo)致系統(tǒng)又掛掉,然后監(jiān)控系統(tǒng)又重啟,這樣會使得很多的請求沒有得到有效的處理,大大降低系統(tǒng)的處理能力。為了保證后臺系統(tǒng)每時每刻都最大限度的發(fā)揮自己的處理能力,當(dāng)負(fù)載超過系統(tǒng)自身的處理能力時,拒絕該請求。拒絕后可以將該請求本地系列化,保存相關(guān)的數(shù)據(jù)發(fā)送到離線數(shù)據(jù)處理系統(tǒng)進(jìn)行處理。
在前一篇文章《海量數(shù)據(jù)處理系列之Java線程池使用》第四節(jié)中有界隊(duì)列線程池使用中有提到這種方式的具體實(shí)現(xiàn)。以上面的系統(tǒng)為例,有界線程池可以這樣配置,corePoolSize為30,maximumPoolSize為50,有界隊(duì)列為ArrayBlockingQueue<Runnable>(100)。這樣系統(tǒng)在處理請求的時候采用如下策略:
1) 當(dāng)一個請求過來,線程池開啟一個線程來處理,直到30個線程都在處理請求。
2) 當(dāng)線程池中沒有空閑線程了,就將請求添加到有界隊(duì)列當(dāng)中,直到隊(duì)列滿為止。
3) 當(dāng)隊(duì)列滿以后,在開啟線程來處理新的請求,直到開啟的線程數(shù)達(dá)到maximumPoolSize。
4) 當(dāng)開啟的線程數(shù)達(dá)到maximumPoolSize后,任務(wù)隊(duì)列又已經(jīng)滿了后,此時再過來的請求將被拒絕,被拒絕的請求在本地系列化,將保存的數(shù)據(jù)同步到離線數(shù)據(jù)系統(tǒng)進(jìn)行處理。
海量數(shù)據(jù)處理都是采用分布式的,每臺機(jī)器的處理能力有限,可以將請求分布到不同的機(jī)器上去。如果每臺機(jī)器被拒絕的請求數(shù)過多的時候,就要考慮添加處理的機(jī)器了。
原文鏈接:http://www.cnblogs.com/cstar/archive/2012/06/25/2561388.html
【編輯推薦】
- 系統(tǒng)架構(gòu)師談企業(yè)應(yīng)用架構(gòu)之開卷有益
- 系統(tǒng)架構(gòu)師談企業(yè)應(yīng)用架構(gòu)之系統(tǒng)建模1
- 系統(tǒng)架構(gòu)師談企業(yè)應(yīng)用架構(gòu)之系統(tǒng)建模2
- 系統(tǒng)架構(gòu)師談企業(yè)應(yīng)用架構(gòu)之系統(tǒng)建模3
- 系統(tǒng)架構(gòu)師談企業(yè)應(yīng)用架構(gòu)之系統(tǒng)建模4
- 系統(tǒng)架構(gòu)師談企業(yè)應(yīng)用架構(gòu)之系統(tǒng)設(shè)計(jì)規(guī)范與原則1
- 系統(tǒng)架構(gòu)師談企業(yè)應(yīng)用架構(gòu)之系統(tǒng)設(shè)計(jì)規(guī)范與原則2
- 系統(tǒng)架構(gòu)師談企業(yè)應(yīng)用架構(gòu)之業(yè)務(wù)邏輯層