新聞中心
前段時(shí)間在網(wǎng)上看到騰訊后臺(tái)開發(fā)總監(jiān)bison分享的一篇文章《淺談過載保護(hù)》,讀來(lái)受益匪淺。剛好自己也在處理系統(tǒng)請(qǐng)求過載的問題,把自己的一些心得體會(huì)總結(jié)出來(lái)拿來(lái)與大家一起探討。

在bison的文章中談到:對(duì)于延時(shí)敏感的服務(wù),當(dāng)外部請(qǐng)求超過系統(tǒng)處理能力,如果系統(tǒng)沒有做相應(yīng)保護(hù),可能導(dǎo)致歷史累計(jì)的超時(shí)請(qǐng)求達(dá)到一定的規(guī)模,像雪球一樣形成惡性循環(huán),由于系統(tǒng)處理的每個(gè)請(qǐng)求都因?yàn)槌瑫r(shí)而無(wú)效,系統(tǒng)對(duì)外呈現(xiàn)的服務(wù)能力為0,且這種情況不能自動(dòng)恢復(fù)。我們的系統(tǒng)就是要盡量避免這種情況的出現(xiàn),下面將詳細(xì)來(lái)分析一個(gè)現(xiàn)實(shí)中的案例。
一 有過載問題的系統(tǒng)
數(shù)據(jù)處理流程:
1) 前端將請(qǐng)求發(fā)送給數(shù)據(jù)解析及轉(zhuǎn)發(fā)系統(tǒng),
2)數(shù)據(jù)解析及轉(zhuǎn)發(fā)系統(tǒng)將封裝好的數(shù)據(jù)發(fā)送后臺(tái)數(shù)據(jù)請(qǐng)求,設(shè)置超時(shí)時(shí)間(假設(shè)300ms),線程同步等待處理結(jié)果從后臺(tái)返回。
3)在300ms內(nèi)正確返回結(jié)果后,則將處理的結(jié)果返回給前端,如果在300ms內(nèi)超時(shí),則將數(shù)據(jù)發(fā)送到一次超時(shí)處理系統(tǒng)(假設(shè)設(shè)置超時(shí)時(shí)間500ms),線程同步等待結(jié)果返回。
4)在500ms內(nèi)正確返回結(jié)果后,則將處理的結(jié)果返回給前端,如果再一次超時(shí),返回一個(gè)默認(rèn)的處理結(jié)果給前端,后端對(duì)數(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),采取的是無(wú)界隊(duì)列線程池的實(shí)現(xiàn),這樣從客戶端來(lái)的請(qǐng)求,會(huì)被這樣處理:
1) 如果線程池中有空閑線程,會(huì)將請(qǐng)求直接交給線程處理。
2) 如果沒有空閑線程,就將請(qǐng)求保存到任務(wù)隊(duì)列。
假設(shè)開50個(gè)線程,每個(gè)線程秒平均處理一個(gè)請(qǐng)求,那么系統(tǒng)每秒可以處理的最大請(qǐng)求數(shù)是50個(gè)。一旦前端數(shù)據(jù)請(qǐng)求超過50個(gè)每秒,在任務(wù)隊(duì)列中將會(huì)堆積大量的請(qǐng)求,前臺(tái)不斷發(fā)送過來(lái),后來(lái)處理不過來(lái),前端又設(shè)置了套接字超時(shí),導(dǎo)致隊(duì)列中的大量請(qǐng)求超時(shí),直接使得后端線程從隊(duì)列中取出套接字解析的時(shí)候,套接字已經(jīng)被前臺(tái)關(guān)閉了,引發(fā)I/O異常。堆積的量一旦雪崩,將使前臺(tái)發(fā)送過來(lái)的請(qǐng)求全部I/O異常,后臺(tái)處理系統(tǒng)跟掛掉無(wú)異了。
二 相對(duì)完善的系統(tǒng)
在上面的系統(tǒng)中,對(duì)請(qǐng)求是來(lái)者不拒的狀態(tài),具體來(lái)講就是將所有的請(qǐng)求都保存到任務(wù)隊(duì)列。請(qǐng)求堆積到一定程度,隊(duì)列中的很多請(qǐng)求都超時(shí),這是可以采取清空請(qǐng)求隊(duì)列的方式,這個(gè)可以通過采取一定的監(jiān)控方式來(lái)實(shí)現(xiàn)。例如上圖中的心跳監(jiān)控模塊,它可以通過這樣的方式來(lái)實(shí)現(xiàn),就是模擬客戶端的請(qǐng)求,每隔一定時(shí)間發(fā)送一些請(qǐng)求過去,如果有大部分都正常返回,說(shuō)明后端處理系統(tǒng)正常;當(dāng)出現(xiàn)大部分超時(shí)的時(shí)候,說(shuō)明后臺(tái)系統(tǒng)已經(jīng)掛掉了,這時(shí)候重啟數(shù)據(jù)解析及轉(zhuǎn)發(fā)系統(tǒng),清空系統(tǒng)中的任務(wù)請(qǐng)求隊(duì)列,這樣可以暫時(shí)處理請(qǐng)求高峰期的情況。
但是這個(gè)方式也是治標(biāo)不治本的,后臺(tái)最多只能處理這么多請(qǐng)求,重啟后照樣會(huì)導(dǎo)致大量堵塞導(dǎo)致系統(tǒng)又掛掉,然后監(jiān)控系統(tǒng)又重啟,這樣會(huì)使得很多的請(qǐng)求沒有得到有效的處理,大大降低系統(tǒng)的處理能力。為了保證后臺(tái)系統(tǒng)每時(shí)每刻都最大限度的發(fā)揮自己的處理能力,當(dāng)負(fù)載超過系統(tǒng)自身的處理能力時(shí),拒絕該請(qǐng)求。拒絕后可以將該請(qǐ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
1) 當(dāng)一個(gè)請(qǐng)求過來(lái),線程池開啟一個(gè)線程來(lái)處理,直到30個(gè)線程都在處理請(qǐng)求。
2) 當(dāng)線程池中沒有空閑線程了,就將請(qǐng)求添加到有界隊(duì)列當(dāng)中,直到隊(duì)列滿為止。
3) 當(dāng)隊(duì)列滿以后,在開啟線程來(lái)處理新的請(qǐng)求,直到開啟的線程數(shù)達(dá)到maximumPoolSize。
4) 當(dāng)開啟的線程數(shù)達(dá)到maximumPoolSize后,任務(wù)隊(duì)列又已經(jīng)滿了后,此時(shí)再過來(lái)的請(qǐng)求將被拒絕,被拒絕的請(qǐng)求在本地系列化,將保存的數(shù)據(jù)同步到離線數(shù)據(jù)系統(tǒng)進(jìn)行處理。
海量數(shù)據(jù)處理都是采用分布式的,每臺(tái)機(jī)器的處理能力有限,可以將請(qǐng)求分布到不同的機(jī)器上去。如果每臺(tái)機(jī)器被拒絕的請(qǐng)求數(shù)過多的時(shí)候,就要考慮添加處理的機(jī)器了。
本文名稱:海量數(shù)據(jù)處理之系統(tǒng)過載保護(hù)
文章起源:http://fisionsoft.com.cn/article/djiogcg.html


咨詢
建站咨詢
