事故描述
2016.10.22.09.30左右驗(yàn)證碼無法顯示,一直超時(shí)。初步調(diào)查發(fā)現(xiàn)2016.10.21日晚上10點(diǎn)已經(jīng)出現(xiàn)此問題。加上往前幾天內(nèi)也發(fā)現(xiàn)了圖片驗(yàn)證碼服務(wù)變慢海洼,偶爾超時(shí)的現(xiàn)象。但是服務(wù)器一直運(yùn)行背稼,未出現(xiàn)宕機(jī)贰军。
事故原因
最終調(diào)查發(fā)現(xiàn),是因?yàn)閳D形驗(yàn)證碼服務(wù)器中緩存的10000張圖片導(dǎo)致了內(nèi)存過多占用(>90%)蟹肘,服務(wù)器實(shí)際的請(qǐng)求處理能力變?nèi)醮侍邸V?個(gè)多月的運(yùn)行沒有暴露是因?yàn)閳D片驗(yàn)證碼用量小。最近用量稍微增大后帘腹,就會(huì)出現(xiàn)此問題贰盗。
事故分析
一開始考慮是并發(fā)量超過處理能力導(dǎo)致服務(wù)器變慢,于是在本地進(jìn)行了壓測阳欲,發(fā)現(xiàn)增大線程確實(shí)可以增大QPS舵盈,然而忽略了JVM運(yùn)行參數(shù),因此結(jié)論并不準(zhǔn)確球化。
后來統(tǒng)計(jì)Nginx訪問量后發(fā)現(xiàn)秽晚,接口調(diào)用在正常值范圍內(nèi),并不多筒愚。
- 查看線上日志后赴蝇,發(fā)現(xiàn)了heap space的OOM錯(cuò)誤,但是并不確定是請(qǐng)求擁塞還是其他原因巢掺。
- 在本地使用和線上相同的JVM參數(shù)后再進(jìn)行壓測句伶,由于生成圖片驗(yàn)證碼池需要一定時(shí)間,所以長時(shí)間壓測后發(fā)現(xiàn)出現(xiàn)相同錯(cuò)誤陆淀。參數(shù)中配置了dump OOM考余。
- 使用MAT工具對(duì)dump文件進(jìn)行分析。以下是MAT推薦分析結(jié)果轧苫。
- 繼續(xù)查看Histogram:
發(fā)現(xiàn)BufferImage占用了近470M內(nèi)存(默認(rèn)啟動(dòng)參數(shù)里設(shè)置的是500M)楚堤。Int數(shù)組為像素?cái)?shù)組。驗(yàn)證碼池剛好占用了91%,并沒有完全占滿浸剩。所以具備一定服務(wù)能力钾军,也是為什么之前運(yùn)行2個(gè)月都沒有暴露的原因。不過請(qǐng)求數(shù)量變多绢要,按道理會(huì)觸發(fā)消耗圖片驗(yàn)證碼,反而是釋放內(nèi)存才對(duì)拗小,確實(shí)重罪,所以上述驗(yàn)證碼對(duì)象并沒有到達(dá)1萬,dump前肯定有一定的消耗,但是由于剩余內(nèi)存已經(jīng)完全不夠當(dāng)時(shí)請(qǐng)求處理需要的內(nèi)存剿配,所以請(qǐng)求處理線程無法創(chuàng)建搅幅,請(qǐng)求一直積壓導(dǎo)致timeout。 請(qǐng)求線程需要內(nèi)存呼胚,同樣也會(huì)釋放內(nèi)存茄唐,每0.5秒觸發(fā)消耗驗(yàn)證碼,但若是0.5秒內(nèi)進(jìn)來的請(qǐng)求所需的內(nèi)存大于省下的內(nèi)存蝇更,就會(huì)造成OOM沪编。由于驗(yàn)證碼對(duì)象個(gè)數(shù)并沒有超過10000,所以初步斷定不是內(nèi)存泄漏導(dǎo)致年扩。但是還需要進(jìn)一步排除蚁廓。
在本地分別對(duì)單獨(dú)循環(huán)生產(chǎn)驗(yàn)證碼邏輯和生成到驗(yàn)證碼池做測試,發(fā)現(xiàn)后者在內(nèi)存一定的情況下厨幻,確實(shí)會(huì)引起OOM相嵌,前者并不會(huì),排除內(nèi)存泄漏况脆。
把JVM的 Xmx參數(shù)改為800M后饭宾,長時(shí)間壓測沒有影響系統(tǒng)穩(wěn)定性,佐證了結(jié)論格了。
事故處理
調(diào)整JVM內(nèi)存大小或者減小圖片驗(yàn)證碼池大小看铆。目前一方面稍微增大了JVM內(nèi)存,并且合理的設(shè)置了圖片驗(yàn)證碼大小笆搓。
事故總結(jié)
之前只是通過最終網(wǎng)頁展示的圖片大行允(<5KB)毛估了1萬張圖片的占用量(約50M),所以認(rèn)為對(duì)內(nèi)存需求并不大满败,事實(shí)是肤频,每張圖片對(duì)應(yīng)的內(nèi)存對(duì)象平均50KB左右(包括無法GC的直接或者間接強(qiáng)引用對(duì)象)。當(dāng)時(shí)得出10000張的結(jié)論也沒有經(jīng)過科學(xué)計(jì)算算墨,也是毛估∠模現(xiàn)在對(duì)其進(jìn)行分析,得到了如下公式:
S = T3/T2 - T3/T1
其中:
S 為驗(yàn)證碼池大小
T1 為生成到驗(yàn)證碼池的周期
T2 為從驗(yàn)證碼池消費(fèi)驗(yàn)證碼的周期
T3 為最大連續(xù)高峰服務(wù)時(shí)間(S值能保證這段時(shí)間內(nèi)不用臨時(shí)生成新的圖片驗(yàn)證碼而浪費(fèi)CPU)
(時(shí)間單位均為s)
巧的是驗(yàn)證碼池剛好接近500M占用净嘀,但是并不完全占用报咳,所以小量運(yùn)行沒有運(yùn)行就出錯(cuò),而且后臺(tái)生成到池子的速度也很慢挖藏,所以短期高并發(fā)壓測也并不會(huì)出現(xiàn)異常暑刃。總之因?yàn)楹芏嗲珊夏っ撸瑘D片驗(yàn)證碼系統(tǒng)僥幸運(yùn)行至今岩臣。在設(shè)計(jì)系統(tǒng)的時(shí)候溜嗜,還是要多一點(diǎn)科學(xué)的精打細(xì)算。