架構(gòu)師才需要知道的知識:如何做容量預(yù)估和調(diào)優(yōu)

為了構(gòu)建高并發(fā)、高可用的系統(tǒng)架構(gòu),壓測、容量預(yù)估必不可少,在發(fā)現(xiàn)系統(tǒng)瓶頸后,需要有針對性地?cái)U(kuò)容、優(yōu)化。結(jié)合樓主的經(jīng)驗(yàn)和知識,本文做一個(gè)簡單的總結(jié),歡迎探討。

1、QPS保障目標(biāo)

一開始就要明確定義QPS保障目標(biāo),以此來推算所需的服務(wù)、存儲(chǔ)資源??筛鶕?jù)歷史同期QPS,或者平時(shí)峰值的2到3倍估算。

壓測目標(biāo)示例:

qps達(dá)到多少時(shí),服務(wù)的負(fù)載正常,如平均響應(yīng)時(shí)間、95分位響應(yīng)時(shí)間、cpu使用率、內(nèi)存使用率、消費(fèi)延遲低于多少

不要讓任何一個(gè)環(huán)節(jié)成為瓶頸,需考慮服務(wù)實(shí)例、數(shù)據(jù)庫、Redis、ES、Hbase等資源

2、服務(wù)注意點(diǎn)

2.1、服務(wù)qps上限

服務(wù)qps上限 = 工作線程數(shù) * 1/平均單次請求處理耗時(shí)

主要關(guān)注以下幾點(diǎn):

(1)工作線程數(shù),對qps起到了直接影響。

dubbo工作線程數(shù)配置舉例:

<dubbo:protocol name="dubbo" threadpool="fixed" threads="1000" />

(2)cpu使用率:跟服務(wù)是I/O密集型,還是計(jì)算密集型有關(guān)。

I/O密集型:調(diào)用多個(gè)下游服務(wù),本身邏輯較簡單,cpu使用率不會(huì)很高,因此服務(wù)實(shí)例的個(gè)數(shù)不用很多

計(jì)算密集型:本身邏輯很復(fù)雜,有較重的計(jì)算,cpu使用率可能飆升,因此可適當(dāng)多部署一些服務(wù)實(shí)例

(3)網(wǎng)絡(luò)帶寬:

對于大量的小請求,基本無需考慮

如果請求內(nèi)容較大,多個(gè)并發(fā)可能打滿網(wǎng)絡(luò)帶寬,如上傳圖片、視頻等。

以實(shí)際壓測為準(zhǔn)?;蛘咴诰€上調(diào)整權(quán)重,引導(dǎo)較多流量訪問1臺實(shí)例,記錄達(dá)到閾值時(shí)的qps,可估算出單實(shí)例的最大qps。

2.2、超時(shí)時(shí)間設(shè)置

漏斗型:從上到下,timeout時(shí)間建議由大到小設(shè)置,也即底層/下游服務(wù)的timeout時(shí)間不宜設(shè)置太大;否則可能出現(xiàn)底層/下游服務(wù)線程池耗盡、然后拒絕請求的問題(拋出

java.util.concurrent.RejectedExecutionException異常)

原因是上游服務(wù)已經(jīng)timeout了,而底層/下游服務(wù)仍在執(zhí)行,上游請求源源不斷打到底層/下游服務(wù),直至線程池耗盡、新請求被拒絕,最壞的情況是產(chǎn)生級聯(lián)的雪崩,上游服務(wù)也耗盡線程池,無法響應(yīng)新請求。

具體timeout時(shí)間,取決于接口的響應(yīng)時(shí)間,可參考95分位、或99分位的響應(yīng)時(shí)間,略微大一些。

dubbo超時(shí)時(shí)間示例:在服務(wù)端、客戶端均可設(shè)置,推薦在服務(wù)端設(shè)置默認(rèn)超時(shí)時(shí)間,客戶端也可覆蓋超時(shí)時(shí)間;

<dubbo:service id="xxxService" interface="com.xxx.xxxService" timeout=1000 />

<dubbo:reference id="xxxService" interface="com.xxx.xxxService" timeout=500 />

2.3、異步并行調(diào)用


如果多個(gè)調(diào)用之間,沒有順序依賴關(guān)系,為了提高性能,可考慮異步并行調(diào)用。

dubbo異步調(diào)用示例:

首先,需要配置consumer.xml,指定接口是異步調(diào)用:<dubbo:reference id="xxxService" interface="com.xxx.xxxService" async=true />

然后,在代碼中通過RpcContext.getContext().getFuture()獲取異步調(diào)用結(jié)果Future對象:

// 調(diào)用1先執(zhí)行? ? interface1.xxx();? ? // 調(diào)用2、3、4無順序依賴,可異步并行執(zhí)行? ? interface2.xxx();? ? future2 = RpcContext.getContext().getFuture();? ? interface3.xxx();? ? future3 = RpcContext.getContext().getFuture();? ? interface4.xxx();? ? future4 = RpcContext.getContext().getFuture();? ? // 獲取調(diào)用2、3、4的執(zhí)行結(jié)果? ? result2 = future2.get();? ? result3 = future3.get();? ? result4 = future4.get();? ? // 此處會(huì)阻塞至調(diào)用2、3、4都執(zhí)行完成,取決于執(zhí)行時(shí)間最長的那個(gè)? ? handleResult2(result2);? ? handleResult3(result3);? ? handleResult4(result4);? ? // 調(diào)用5最后執(zhí)行,會(huì)阻塞至前序操作都完成? ? interface5.xxx();

2.4、強(qiáng)依賴、弱依賴

強(qiáng)依賴調(diào)用:決不能跳過,失敗則拋異常、快速失敗

弱依賴調(diào)用:決不能阻塞流程,失敗可忽略

2.5 降級

粗粒度:開關(guān)控制,如對整個(gè)非關(guān)鍵功能降級,隱藏入口

細(xì)粒度:調(diào)用下游接口失敗時(shí),返回默認(rèn)值

2.6 限流

超過的部分直接拋限流異常,萬不得已為之。

3、存儲(chǔ)資源注意點(diǎn)

3.1、放大倍數(shù):1次核心操作,對應(yīng)的資源讀寫次數(shù)、接口調(diào)用次數(shù)

例如:1次核心操作,查了3次緩存、寫了1次緩存、查了2次數(shù)據(jù)庫、寫了1次數(shù)據(jù)庫、發(fā)了1次MQ消息、調(diào)了下游服務(wù)A的接口;

則對于讀緩存放大倍數(shù)為3,寫緩存放大倍數(shù)為1,讀數(shù)據(jù)庫放大倍數(shù)為2,寫數(shù)據(jù)庫放大倍數(shù)為1,MQ放大倍數(shù)為1,調(diào)用下游服務(wù)A的放大倍數(shù)為1。針對寫放大倍數(shù),需要單獨(dú)考慮主庫是否扛得住放大倍數(shù)的qps。

需關(guān)注:

讀、寫的放大倍數(shù),要分開考慮,因?yàn)榉植际郊軜?gòu)通常是一主多從,一主需要支撐所有的寫QPS,多從可以支撐所有的讀QPS

? ? DB讀放大倍數(shù)、DB寫放大倍數(shù)

? ? Redis讀放大倍數(shù)、Redis寫放大倍數(shù)

? ? MQ放大倍數(shù)

? ? 接口調(diào)用放大倍數(shù)等

3.2、存儲(chǔ)資源QPS估算

存儲(chǔ)資源的QPS上限,跟機(jī)器的具體配置有關(guān),8C32G機(jī)型的QPS上限當(dāng)然要高于4C16G機(jī)型。下表為典型值舉例。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容