微服務(wù)高可用的兩個(gè)關(guān)鍵技巧,你一定用得上
概述
這篇文章我們來聊聊在微服務(wù)架構(gòu)中,到底如何保證整套系統(tǒng)的高可用?
排除掉一些基礎(chǔ)設(shè)施的故障,比如說Redis集群掛了,Elasticsearch集群故障了,MySQL宕機(jī)。
微服務(wù)架構(gòu)本身最最核心的保障高可用的措施,就是兩點(diǎn):
- 一個(gè)是基于Hystrix做資源隔離以及熔斷;
- 另一個(gè)是做備用降級方案。
如果資源隔離和降級都做的很完善,那么在雙11這種高并發(fā)場景下,也許可能會(huì)出現(xiàn)個(gè)別的服務(wù)故障,但是絕不會(huì)蔓延到整個(gè)系統(tǒng)全部宕機(jī)。
業(yè)務(wù)場景介紹
大家首先回顧一下下面這張圖,這是上篇文章中說到的一個(gè)公司的系統(tǒng)。
如上圖,核心服務(wù)A調(diào)用了核心服務(wù)B和C,在核心服務(wù)B響應(yīng)過慢時(shí),會(huì)導(dǎo)致核心服務(wù)A的某個(gè)線程池全部卡死。
但是此時(shí)因?yàn)槟阌昧薶ystrix做了資源隔離,所以核心服務(wù)A是可以正常調(diào)用服務(wù)C的,那么就可以保證用戶起碼是可以使用APP的部分功能的,只不過跟服務(wù)B關(guān)聯(lián)的頁面刷不出來,功能無法使用罷了。
當(dāng)然這種情況在生產(chǎn)系統(tǒng)中,是絕對不被允許的,所以大家不要讓上述情況發(fā)生。
在上一篇文章中,我們最終把系統(tǒng)優(yōu)化成了下圖這樣:
- 要保證一個(gè)hystrix線程池可以輕松處理每秒鐘的請求。
- 同時(shí)還有合理的超時(shí)時(shí)間設(shè)置,避免請求太慢卡死線程。
線上經(jīng)驗(yàn)—如何設(shè)置Hystrix線程池大小
好,現(xiàn)在問題來了,在生產(chǎn)環(huán)境中,我們到底應(yīng)該如何設(shè)置服務(wù)中每個(gè)hystrix線程池的大???
下面是我們在線上經(jīng)過了大量系統(tǒng)優(yōu)化后的生產(chǎn)經(jīng)驗(yàn)總結(jié):
假設(shè)你的服務(wù)A,每秒鐘會(huì)接收30個(gè)請求,同時(shí)會(huì)向服務(wù)B發(fā)起30個(gè)請求,然后每個(gè)請求的響應(yīng)時(shí)長經(jīng)驗(yàn)值大概在200ms,那么你的hystrix線程池需要多少個(gè)線程呢?
計(jì)算公式是:30(每秒請求數(shù)量) * 0.2(每個(gè)請求的處理秒數(shù)) + 4(給點(diǎn)緩沖buffer) = 10(線程數(shù)量)。
如果對上述公式存在疑問,不妨反過來推算一下,為什么10個(gè)線程可以輕松抗住每秒30個(gè)請求?
一個(gè)線程200毫秒可以執(zhí)行完一個(gè)請求,那么一個(gè)線程1秒可以執(zhí)行5個(gè)請求,理論上,只要6個(gè)線程,每秒就可以執(zhí)行30個(gè)請求。
也就是說,線程里的10個(gè)線程中,就6個(gè)線程足以抗住每秒30個(gè)請求了。剩下4個(gè)線程都在玩兒,空閑著。
那為啥要多搞4個(gè)線程呢?很簡單,因?yàn)槟阋粢稽c(diǎn)buffer空間。
萬一在系統(tǒng)高峰期,系統(tǒng)性能略有下降,此時(shí)不少請求都耗費(fèi)了300多毫秒才執(zhí)行完,那么一個(gè)線程每秒只能處理3個(gè)請求了,10個(gè)線程剛剛好勉強(qiáng)可以hold住每秒30個(gè)請求。所以你必須多考慮留幾個(gè)線程。
老規(guī)矩,給大家來一張圖,直觀的感受一下整個(gè)過程。
線上經(jīng)驗(yàn)—如何設(shè)置請求超時(shí)時(shí)間
線程數(shù)量OK了,那么請求的超時(shí)時(shí)間設(shè)置為多少?答案是300毫秒。
為啥呢?很簡單啊,如果你的超時(shí)時(shí)間設(shè)置成了500毫秒,想想可能會(huì)有什么后果?
考慮極端情況,如果服務(wù)B響應(yīng)變慢,要500毫秒才響應(yīng),你一個(gè)線程每秒最多只能處理2個(gè)請求了,10個(gè)線程只能處理20個(gè)請求。
而每秒是30個(gè)請求過來,結(jié)局會(huì)如何?
咱們回看一下第一張圖就知道了,大量的線程會(huì)全部卡死,來不及處理那么多請求,最后用戶會(huì)刷不出來頁面。
還是有點(diǎn)不理解?再給你一張圖,讓你感受一下這個(gè)不合理的超時(shí)時(shí)間導(dǎo)致的問題!
?如果你的線程池大小和超時(shí)時(shí)間沒有配合著設(shè)置好,很可能會(huì)導(dǎo)致服務(wù)B短暫的性能波動(dòng),瞬間導(dǎo)致服務(wù)A的線程池卡死,里面的線程要卡頓一段時(shí)間才能繼續(xù)執(zhí)行下一個(gè)請求。
哪怕一段時(shí)間后,服務(wù)B的接口性能恢復(fù)到200毫秒以內(nèi)了,服務(wù)A的線?程池里卡死的狀況也要好一會(huì)兒才能恢復(fù)過來。
你的超時(shí)時(shí)間設(shè)置的越不合理,比如設(shè)置的越長,設(shè)置到了1秒、2秒,那么這種卡死的情況就需要越長的時(shí)間來恢復(fù)。
所以說,此時(shí)你的超時(shí)時(shí)間得設(shè)置成300毫秒,保證一個(gè)請求300毫秒內(nèi)執(zhí)行不完,立馬超時(shí)返回。
這樣線程池里的線程不會(huì)長時(shí)間卡死,可以有條不紊的處理多出來的請求,大不了就是300毫秒內(nèi)處理不完立即超時(shí)返回,但是線程始終保持可以運(yùn)行的狀態(tài)。
這樣當(dāng)服務(wù)B的接口性能恢復(fù)到200毫秒以內(nèi)后,服務(wù)A的線程池里的線程很快就可以恢復(fù)。
這就是生產(chǎn)系統(tǒng)上的hystrix參數(shù)設(shè)置優(yōu)化經(jīng)驗(yàn),你需要考慮到各種參數(shù)應(yīng)該如何設(shè)置。
否則的話,很可能會(huì)出現(xiàn)上文那樣的情況,用了高大上的Spring Cloud架構(gòu),結(jié)果跟黑盒子一樣,莫名其妙系統(tǒng)故障,各種卡死,宕機(jī)什么的。
好了,我們繼續(xù)。如果現(xiàn)在這套系統(tǒng)每秒有6000請求,然后核心服務(wù)A一共部署了60臺機(jī)器,每臺機(jī)器就是每秒會(huì)收到100個(gè)請求,那么此時(shí)你的線程池需要多少個(gè)線程?
很簡單,10個(gè)線程抗30個(gè)請求,30個(gè)線程抗100請求,差不多了吧。
這個(gè)時(shí)候,你應(yīng)該知道服務(wù)A的線程池調(diào)用服務(wù)B的線程池分配多少線程了吧?超時(shí)時(shí)間如何設(shè)置應(yīng)該也知道了!
其實(shí)這個(gè)東西不是固定死的,但是你要知道他的計(jì)算方法。
根據(jù)服務(wù)的響應(yīng)時(shí)間、系統(tǒng)高峰QPS、有多少臺機(jī)器,來計(jì)算出來,線程池的大小以及超時(shí)時(shí)間!
服務(wù)降級
設(shè)置完這些后,就應(yīng)該要考慮服務(wù)降級的事了。
如果你的某個(gè)服務(wù)掛了,那么你的hystrix會(huì)走熔斷器,然后就會(huì)降級,你需要考慮到各個(gè)服務(wù)的降級邏輯。
舉一些常見的例子:
- 如果查詢數(shù)據(jù)的服務(wù)掛了,你可以查本地的緩存。
- 如果寫入數(shù)據(jù)的服務(wù)掛了,你可以先把這個(gè)寫入操作記錄日志到比如mysql里,或者寫入MQ里,后面再慢慢恢復(fù)。
- 如果redis掛了,你可以查mysql。
- 如果mysql掛了,你可以把操作日志記錄到es里去,后面再慢慢恢復(fù)數(shù)據(jù)。
具體用什么降級策略,要根據(jù)業(yè)務(wù)來定,不是一成不變的。
總結(jié)
最后總結(jié)一下,排除那些基礎(chǔ)設(shè)施的故障,你要玩兒微服務(wù)架構(gòu)的話,需要保證兩點(diǎn):
- 首先你的hystrix資源隔離以及超時(shí)這塊,必須設(shè)置合理的參數(shù),避免高峰期,頻繁的hystrix線程卡死。
- 其次,針對個(gè)別的服務(wù)故障,要設(shè)置合理的降級策略,保證各個(gè)服務(wù)掛了,可以合理的降級,系統(tǒng)整體可用!