1. 
          

          1. 新聞動(dòng)態(tài)

            線(xiàn)上四臺機器同一時(shí)間全部 OOM,到底發(fā)生了什么?

            常見(jiàn)問(wèn)題 發(fā)布者:cya 2019-12-25 08:40 訪(fǎng)問(wèn)量:128

            作者:碼海

            鏈接https://mp.weixin.qq.com/s/B1EplSO2hTEoDAcPNtsGwA



            圣誕快樂(lè )





            案發(fā)現場(chǎng)


            昨天晚上突然短信收到 APM (即 Application Performance Management 的簡(jiǎn)稱(chēng),我們內部自己搭建了這樣一套系統來(lái)對應用的性能、可靠性進(jìn)行線(xiàn)上的監控和預警的一種機制)大量告警 畫(huà)外音: 監控是一種非常重要的發(fā)現問(wèn)題的手段,沒(méi)有的話(huà)一定要及時(shí)建立哦
            緊接著(zhù)運維打來(lái)電話(huà)告知線(xiàn)上部署的四臺機器全部 OOM (out of memory, 內存不足),服務(wù)全部不可用,趕緊查看問(wèn)題!

            問(wèn)題排查




            首先運維先重啟了機器,保證線(xiàn)上服務(wù)可用,然后再仔細地看了下線(xiàn)上的日志,確實(shí)是因為 OOM 導致服務(wù)不可用
            第一時(shí)間想到 dump 當時(shí)的內存狀態(tài),但由于為了讓線(xiàn)上盡快恢復服務(wù),運維重啟了機器,導致無(wú)法 dump 出事發(fā)時(shí)的內存。所以我又看了下我們 APM 中對 JVM 的監控圖表 
            畫(huà)外音:一種方式不行,嘗試另外的角度切入!再次強調,監控非常重要!完善的監控能還原當時(shí)的事發(fā)現場(chǎng),方便定位問(wèn)題。
            不看不知道,一看嚇一跳,從 16:00 開(kāi)始應用中創(chuàng )建的線(xiàn)程居然每時(shí)每刻都在上升,一直到 3w 左右,重啟后(藍色箭頭),線(xiàn)程也一直在不斷增長(cháng)),正常情況下的線(xiàn)程數是多少呢,600!問(wèn)題找到了,應該是在下午 16:00 左右發(fā)了一段有問(wèn)題的代碼,導致線(xiàn)程一直在創(chuàng )建,且創(chuàng )建的線(xiàn)程一直未消亡!查看發(fā)布記錄,發(fā)現發(fā)布記錄只有這么一段可疑的代碼 diff:在 HttpClient 初始化的時(shí)候額外加了一個(gè) evictExpiredConnections 配置
            問(wèn)題定位了,應該是就是這個(gè)配置導致的!(線(xiàn)程上升的時(shí)間點(diǎn)和發(fā)布時(shí)間點(diǎn)完全吻合!),于是先把這個(gè)新加的配置給干掉上線(xiàn),上線(xiàn)之后線(xiàn)程數果然恢復正常了。那 evictExpiredConnections 做了什么導致線(xiàn)程數每時(shí)每刻在上升呢?這個(gè)配置又是為了解決什么問(wèn)題而加上的呢?于是找到了相關(guān)同事來(lái)了解加這個(gè)配置的前因后果

            還原事發(fā)經(jīng)過(guò)


            最近線(xiàn)上出現不少 NoHttpResponseException 的異常, 而上面那個(gè)配置就是為了解決這個(gè)異常而添加的,那是什么導致了這個(gè)異常呢?
            在說(shuō)這個(gè)問(wèn)題之前我們得先了解一下 http 的 keep-alive 機制。
            先看下正常的一個(gè) TCP 連接的生命周期
            可以看到每個(gè) TCP 連接都要經(jīng)過(guò)三次握手建立連接后才能發(fā)送數據,要經(jīng)過(guò)四次揮手才能斷開(kāi)連接,如果每個(gè) TCP 連接在 server 返回 response 后都立馬斷開(kāi),則發(fā)起多個(gè) HTTP 請求就要多次創(chuàng )建斷開(kāi) TCP, 這在 Http 請求很多的情況下無(wú)疑是很耗性能的, 如果在 server 返回 response 不立即斷開(kāi) TCP 鏈接,而是復用這條鏈接進(jìn)行下一次的 Http 請求,則無(wú)形中省略了很多創(chuàng )建 / 斷開(kāi) TCP 的開(kāi)銷(xiāo),性能上無(wú)疑會(huì )有很大提升。
            如下圖示,左圖是不復用 TCP 發(fā)起多個(gè) HTTP 請求的情況,右圖是復用 TCP 的情況,可以看到發(fā)起三次 HTTP 請求,復用 TCP 的話(huà)可以省去兩次建立 / 斷開(kāi) TCP 的開(kāi)銷(xiāo),理論上一個(gè)應用只要開(kāi)啟一個(gè) TCP 連接即可,其他 HTTP 請求都可以復用這個(gè) TCP 連接,這樣 n 次 HTTP 請求可以省去 n-1 次創(chuàng )建 / 斷開(kāi) TCP 的開(kāi)銷(xiāo)。這對性能的提升無(wú)疑是有巨大的幫助。
            回過(guò)頭來(lái)看 keep-alive (又稱(chēng)持久連接,連接復用)做的就是復用連接, 保證連接持久有效。
            畫(huà)外音: Http 1.1 之后 keep-alive 默認支持并開(kāi)啟,目前大部分網(wǎng)站都用了 http 1.1 了,也就是說(shuō)大部分都默認支持連接復用了
            天下沒(méi)有免費的午餐 ,雖然 keep-alive 省去了很多不必要的握手/揮手操作,但由于連接長(cháng)期?;?,如果一直沒(méi)有 http 請求的話(huà),這條連接也就長(cháng)期閑著(zhù)了,會(huì )占用系統資源,有時(shí)反而會(huì )比復用連接帶來(lái)更大的性能消耗。所以我們一般會(huì )為 keep-alive 設置一個(gè) timeout, 這樣如果連接在設置的 timeout 時(shí)間內一直處于空閑狀態(tài)(未發(fā)生任何數據傳輸),經(jīng)過(guò) timeout 時(shí)間后,連接就會(huì )釋放,就能節省系統開(kāi)銷(xiāo)。
            看起來(lái)給 keep-alive 加 timeout 是完美了,但是又引入了新的問(wèn)題(一波已平,一波又起!),考慮如下情況:
            如果服務(wù)端關(guān)閉連接,發(fā)送 FIN 包(注:在設置的 timeout 時(shí)間內服務(wù)端如果一直未收到客戶(hù)端的請求,服務(wù)端會(huì )主動(dòng)發(fā)起帶 FIN 標志的請求以斷開(kāi)連接釋放資源),在這個(gè) FIN 包發(fā)送但是還未到達客戶(hù)端期間,客戶(hù)端如果繼續復用這個(gè) TCP 連接發(fā)送 HTTP 請求報文的話(huà),服務(wù)端會(huì )因為在四次揮手期間不接收報文而發(fā)送 RST 報文給客戶(hù)端,客戶(hù)端收到 RST 報文就會(huì )提示異常 (即 NoHttpResponseException)
            我們再用流程圖仔細梳理一下上述這種產(chǎn)生 NoHttpResponseException 的原因,這樣能看得更明白一些
            費了這么大的功夫,我們終于知道了產(chǎn)生 NoHttpResponseException 的原因,那該怎么解決呢,有兩種策略


            1. 重試,收到異常后,重試一兩次,由于重試后客戶(hù)端會(huì )用有效的連接去請求,所以可以避免這種情況,不過(guò)一次要注意重試次數,避免引起雪崩!
            2. 設置一個(gè)定時(shí)線(xiàn)程,定時(shí)清理上述的閑置連接,可以將這個(gè)定時(shí)時(shí)間設置為 keep alive timeout 時(shí)間的一半以保證超時(shí)前回收。


            evictExpiredConnections 就是用的上述第二種策略,來(lái)看下官方用法使用說(shuō)明


            Makes this instance of HttpClient proactively evict idle connections from the
            connection pool using a background thread.


            調用這個(gè)方法只會(huì )產(chǎn)生一個(gè)定時(shí)線(xiàn)程,那為啥應用中線(xiàn)程會(huì )一直增加呢,因為我們對每一個(gè)請求都創(chuàng )建了一個(gè) HttpClient! 這樣由于創(chuàng )建每一個(gè) HttpClient 實(shí)例j時(shí)都會(huì )調用  evictExpiredConnections ,導致有多少請求就會(huì )創(chuàng )建多少個(gè)定時(shí)線(xiàn)程!
            還有一個(gè)問(wèn)題,為啥線(xiàn)上四臺機器幾乎同一時(shí)間點(diǎn)全掛呢?因為由于負載均衡,這四臺機器的權重是一樣的,硬件配置也一樣,收到的請求其實(shí)也可以認為是差不多的,這樣這四臺機器由于創(chuàng )建 HttpClient 而生成的后臺線(xiàn)程也在同一時(shí)間達到最高點(diǎn),然后同時(shí) OOM。

            問(wèn)題解決


            所以針對以上提到的問(wèn)題,我們首先把 HttpClient 改成了單例,這樣保證服務(wù)啟動(dòng)后只會(huì )有一個(gè)定時(shí)清理線(xiàn)程,另外我們也讓運維針對應用的線(xiàn)程數做了監控,如果超過(guò)某個(gè)閾值直接告警,這樣能在應用 OOM 前及時(shí)發(fā)現處理。
            畫(huà)外音:再次強調,監控相當重要,能把問(wèn)題扼殺在搖籃里!

            總結


            本文通過(guò)線(xiàn)上四臺機器同時(shí) OOM 的現象,來(lái)詳細剖析定位了產(chǎn)生問(wèn)題的原因,可以看到我們在應用某個(gè)庫時(shí)首先要對這個(gè)庫要有充分的了解(上述 HttpClient 的創(chuàng )建不用單例顯然是個(gè)問(wèn)題),其次必要的網(wǎng)絡(luò )知識還是需要的,所以要成為一個(gè)合格的程序員,不光對語(yǔ)言本身有所了解,還要對網(wǎng)絡(luò ),數據庫等也要有所涉獵,這些對排查問(wèn)題以及性能調優(yōu)等會(huì )有非常大的幫助,再次,完善的監控非常重要,通過(guò)觸發(fā)某個(gè)閾值提前告警,可以將問(wèn)題扼殺在搖籃里!




            關(guān)鍵字: OOM問(wèn)題 開(kāi)封網(wǎng)站建設

            文章連接: http://www.gostscript.com/cjwt/656.html

            版權聲明:文章由 晨展科技 整理收集,來(lái)源于互聯(lián)網(wǎng)或者用戶(hù)投稿,如有侵權,請聯(lián)系我們,我們會(huì )立即刪除。如轉載請保留

            双腿国产亚洲精品无码不卡|国产91精品无码麻豆|97久久久久久久极品|无码人妻少妇久久中文字幕
                1.