性能&穩(wěn)定性
性能
- 上緩存:訂閱binlog寫redis裳扯,通過監(jiān)控找出大流量的上游調(diào)用方他嫡,新增一套緩存接口昔逗,讓他們切接口
- 清理慢SQL:是新建索引還是合并索引,如果MySQL搞不定就用ES绸硕,中間也曾探索過用MySQL解決深度分頁
- 接口邏輯修改:商品的緩存查詢接口用到redis mget去批量獲取,之前不分批次一把梭胞此,發(fā)現(xiàn)極端場景下臣咖,上游一次性查詢200個商品會阻塞Redis造成毛刺,后來分成50個一批次好了很多漱牵;還有有些場景要去除事務(wù)夺蛇,比如方法中有RPC請求的時候,會把事務(wù)拉的很長引發(fā)一些問題
- 日志處理:
一開始的時候酣胀,公司common包的日志冗余度高刁赦,還有些日志超長不截斷,一個小時單機就有七八個G的日志闻镶,寫攔截器把日志精簡到1G甚脉;
還有異步打印日志,不過要處理好traceId的透傳問題铆农,之前線上也出過問題牺氨,商品中臺一發(fā)版中央庫存就超時報警,超時接口很簡單就查一下redis墩剖,只有小部分機器接口超時猴凹,而Redis一切正常。經(jīng)過仔細(xì)排查發(fā)現(xiàn)岭皂,有些容器的物理機磁盤有瑕疵郊霎,發(fā)版的時候磁盤IO一般比較高,磁盤問題暴露會嚴(yán)重阻塞爷绘,在同步打印日志時书劝,把用戶請求阻塞了,容器漂移到其他物理機就好了土至。- gc調(diào)優(yōu):選擇合適的垃圾回收器购对,合理調(diào)整gc線程數(shù),還有新生代/老年代比例陶因,Eden區(qū)和Survivor區(qū)的比例骡苞,得到一個可以接受的gc頻次和暫停時間,C端要避免Full gc。
比如:我剛來橙心的時候烙如,有上游反饋接口經(jīng)常超時么抗,排查之后發(fā)現(xiàn)young gc頻次平均20秒一次這個正常,但是每次young gc耗時都要上百ms這個不正常亚铁,一般來說4核8G的容器young gc耗時也就20ms蝇刀。jstack發(fā)現(xiàn)gc線程數(shù)竟然有四五十個太多了,啟動腳本沒有主動配置gc線程數(shù)(ParallelGCThreads)徘溢,那jdk8的老版本有自己默認(rèn)的計算公式吞琐,跟cpu核數(shù)有關(guān),但是docker容器化場景下然爆,取的不是容器的cpu核數(shù)站粟,而是物理機的cpu核數(shù),而我們的物理機有64核曾雕,所以計算出來的gc線程數(shù)就是四五十個奴烙。把gc線程數(shù)寫死成4個,問題立馬就好了剖张,停頓時間縮短到20ms
穩(wěn)定性
所謂穩(wěn)定性切诀,就是不要被上游搞死,自己呢不作死搔弄,也不要把下游搞死
【壓測&限流】
壓測就是摸底幅虑,限流就是自我保護集群壓測:壓一下核心接口性能瓶頸——到底數(shù)據(jù)庫容量不夠還是容器數(shù)量不夠,摸個底
比如:
- 查詢接口的瓶頸一般是docker容器不夠顾犹,當(dāng)大流量來臨可以臨時擴容:
- 寫接口的瓶頸一般是數(shù)據(jù)庫TPS不夠倒庵。比如下單扣庫存,我們以前的做法是Redis扣庫存然后同步在MySQL插一條冪等記錄炫刷,結(jié)果TPS壓到3000多擎宝,主從延遲很嚴(yán)重了;后來改成Redis加鎖Redis扣庫存柬唯,可以壓到1萬多(數(shù)據(jù)庫沒有拆片认臊,拆片之后還可以翻幾倍)圃庭。
- 集群壓測的細(xì)節(jié)也有很多:
比如按照1000的步長逐步增壓锄奢,還是一瞬間從2000漲到1萬,都要測剧腻。因為秒殺場景就是徒增拘央,日常場景就是穩(wěn)步增壓。很多時候逐步增壓能到1萬TPS书在,但是從2000直接攀升到1萬灰伟,集群可能會卡頓,要找到原因做優(yōu)化,優(yōu)化不動了就限流兜底栏账。
而且線上的流量模型是復(fù)雜多變的帖族,單接口壓測或者幾個接口同時壓測,并不能百分百模擬線上流量挡爵,這些需要專業(yè)的qa同學(xué)去搞竖般,可以通過流量錄制和回放做壓測
哨兵壓測:針對單個容器,壓一下單機的極限茶鹃,也可以用來預(yù)估集群數(shù)量涣雕。比如單機極限QPS500,那理論上集群要達到10萬的QPS闭翩,只需要200臺機器就夠了挣郭,再預(yù)留30%的余量
比如:
其他99臺機器都是100的流量權(quán)重,而某一臺機器的權(quán)重加到300疗韵、400兑障,慢慢增加,看一下cpu idle蕉汪、Tomcat繁忙線程數(shù)旺垒、接口RT、GC等指標(biāo)肤无,在不影響上游的前提下他的極限先蒋。
【監(jiān)控&有效報警】兩類指標(biāo)&3個大盤&滅火圖
線上問題,盡量在用戶或上游反饋之前發(fā)現(xiàn)問題宛渐,第一時間在穩(wěn)定性大群里通報竞漾,讓相關(guān)人員緊急介入。報警來源可以是監(jiān)控指標(biāo)窥翩,也可以是日志埋點业岁,或者ERROR日志采集監(jiān)控:
- 系統(tǒng)指標(biāo)監(jiān)控:cpu idle,線程數(shù)寇蚊,jvm指標(biāo)笔时,核心接口QPS/RT同比變化異常,mysql仗岸、Redis允耿、MQ等監(jiān)控
- 業(yè)務(wù)指標(biāo)監(jiān)控:下單成功率同比下降多少,一般通過錯誤碼或日志埋點統(tǒng)計
- 監(jiān)控大盤:通用的監(jiān)控大盤(系統(tǒng)指標(biāo)監(jiān)控+業(yè)務(wù)指標(biāo)監(jiān)控)——日常巡檢扒怖,發(fā)版監(jiān)控大盤(因為機器數(shù)量太多较锡,通用大盤看不清),快速排錯的監(jiān)控大盤(常見的問題指標(biāo)都會配置到大盤里盗痒,快速解決過很多問題)
比如:有一次蚂蕴,發(fā)現(xiàn)一臺C端的機器full gc異常,平均2小時一次full gc,而其他機器好幾天都不會有一次full gc骡楼。解決過程很簡單熔号,先把大盤時間段縮小到第一次full gc前后兩個小時,很快發(fā)現(xiàn)這臺機器的線程數(shù)在某個時間點從200爆漲到1000鸟整;然后時間再聚焦到線程徒增的那幾分鐘跨嘉,發(fā)現(xiàn)cpu假死了一會,把線程一下子卡滿了吃嘿。
如果沒有排錯大盤祠乃,這個問題排查比較困難,單憑經(jīng)驗兑燥,很難想到full gc異常是因為線程數(shù)過多導(dǎo)致的亮瓷,而線程數(shù)過多是因為cpu假死導(dǎo)致的,而且在兩天的時間內(nèi)線程數(shù)一直降不下來降瞳,當(dāng)然里邊細(xì)節(jié)很多嘱支,還牽扯到gc日志和Tomcat線程回收機制,暫時不展開了挣饥。
【故障注入演練】
屬于混沌工程的領(lǐng)域除师,在公司的放火平臺做故障注入,并觀測系統(tǒng)及上下游的反應(yīng)扔枫,找出薄弱環(huán)節(jié)汛聚。
目標(biāo):提前暴露問題并給出預(yù)案,不要等到線上出事故了才被動止損短荐,上醫(yī)治未病倚舀,防患于未然。
比如:
1.切斷第三方鏈路
2.人為把主從延遲加劇忍宋,看一下異常痕貌,很多人代碼里喜歡先update再select,一旦沒有事務(wù)select就容易查出舊數(shù)據(jù)
3.核心接口RT從正常20ms延遲到100ms糠排,看下上游報警和降級機制是否完善舵稠,超時會不會把他們線程池打滿項目拖垮
4.上游請求做高并發(fā)的重放校驗冪等機制
總結(jié):故障注入測試風(fēng)險高,而且耗費精力入宦,最好是技術(shù)評審的時候哺徊,通過經(jīng)驗指出問題,在代碼開發(fā)和自測階段盡量做好云石。很多厲害的系統(tǒng)唉工,會不定時的自動做故障注入實驗研乒,校驗系統(tǒng)的健壯性汹忠,倒逼著程序員在開發(fā)階段就主動考慮這些場景,行成正向循環(huán)。
【平滑發(fā)版&灰度發(fā)布】
- 沒有快速回滾的方案宽菜,不允許上線谣膳!重要變更要做好灰度發(fā)布!
- 發(fā)版不要有正常業(yè)務(wù)的請求毛刺铅乡,公司的LVS負(fù)載均衡還有開源的springboot都不支持流量的權(quán)重預(yù)熱继谚,導(dǎo)致項目啟動成功后,流量是正常進入阵幸,之前單機200QPS那流量進場時也是200QPS花履,會有很多RT毛刺。我們一發(fā)版挚赊,上游就報警了诡壁。
我臨時用了個笨方法,在發(fā)版腳本里邊荠割,把大流量的接口手動curl調(diào)用幾輪妹卿,然后再退出發(fā)版腳本,流量再進場的時候蔑鹦,就沒那么大的毛刺了夺克,方法很笨但效果還不錯
【熔斷&降級】
- 之前在商品中臺基本是C端請求的最下游了,主要考慮Redis高延遲場景下的降級就好嚎朽,我們可以切換到集團自研的Fusion數(shù)據(jù)庫铺纽。
- 我現(xiàn)在負(fù)責(zé)的獎勵中心,是一個發(fā)獎的聚合中心哟忍,上游所有的任務(wù)和活動發(fā)獎室囊,都走獎勵中心,而獎勵中心去統(tǒng)一對接下游的風(fēng)控中心魁索、觸達中心融撞,還有很多真正發(fā)獎的下游:權(quán)益、清結(jié)算粗蔚、微信尝偎、網(wǎng)約車等。
作為一個發(fā)獎的聚合中心對熔斷降級要求很高鹏控,因為下游很多致扯,所以用了hystrix的線程池隔離方案,配好每一個下游的超時時間和錯誤窗口防止被下游拖死当辐。
風(fēng)控接口掛了抖僵,如果某些獎勵在這種場景下是允許跳過風(fēng)控的,那可以自動降級跳過
如果某類獎勵在發(fā)放過程中出現(xiàn)問題缘揪,需要緊急攔截止損耍群,就需要留下后門人工降級跳過
如果某類獎勵在發(fā)放過程中一直失敗义桂,導(dǎo)致MQ不斷重試積壓,影響其他獎勵的發(fā)放蹈垢,也需要留下后門做人工降級跳過
【B/C端分離】
B端業(yè)務(wù)容易造成full gc慷吊,比如定時任務(wù)或者后門接口刷數(shù)據(jù),處理不好就容易滋生問題曹抬。而且B端業(yè)務(wù)變化頻繁溉瓶,經(jīng)常發(fā)版,影響C端正常業(yè)務(wù)的性能和穩(wěn)定性谤民,所以業(yè)務(wù)到一定規(guī)模堰酿,就要B/C端分離。甚至一個B端系統(tǒng)张足,還要拆分出穩(wěn)定查詢類的B端系統(tǒng)和需求變化頻繁的B端系統(tǒng)胞锰,算是一種動靜分離。
【復(fù)盤】
所有的線上事故兢榨,都要做深刻的復(fù)盤嗅榕,所有時間軸和事件要描述清楚,類似問題不允許再發(fā)生
binlog方式更新緩存
背景:幾十處商品修改的代碼吵聪,業(yè)務(wù)發(fā)展前期工單刷數(shù)據(jù)的行為非常頻繁凌那。
- 消費速度3000QPS,生產(chǎn)速度200不到吟逝,正常延遲時間在50ms以內(nèi)帽蝶,控制好生產(chǎn)速度即可,提工單刷數(shù)據(jù)是500個一批次然后sleep1秒块攒。傳統(tǒng)方案中励稳,改數(shù)據(jù)庫刪緩存,中間也會有毫秒級的時間差數(shù)據(jù)不一致
- 數(shù)據(jù)實時性要求高的接口不走緩存接口囱井。比如下單結(jié)算接口
- 解決了傳統(tǒng)方案中驹尼,提工單刷數(shù)據(jù)不走代碼,緩存長時間不失效的問題庞呕,除非自己寫后門接口刷
- 原則上新翎,上架中的商品B端不允許修改,要修改需重新上架
- 下單扣的是Redis庫存
- 未來重構(gòu)的時候住练,可以考慮換成傳統(tǒng)方案地啰,但運行了1年發(fā)現(xiàn)還是挺穩(wěn)的,或者兩種方案可以混用讲逛,緩存延遲敏感的場景下使用傳統(tǒng)方案