<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          HttpClient 設(shè)置不當(dāng)引發(fā)的一次雪崩!

          共 3413字,需瀏覽 7分鐘

           ·

          2021-07-27 08:11

          一. 事件背景

          我最近運(yùn)維了一個(gè)網(wǎng)上的實(shí)時(shí)接口服務(wù),最近經(jīng)常出現(xiàn)Address already in use (Bind failed)的問(wèn)題。

          很明顯是一個(gè)端口綁定沖突的問(wèn)題,于是大概排查了一下當(dāng)前系統(tǒng)的網(wǎng)絡(luò)連接情況和端口使用情況,發(fā)現(xiàn)是有大量time_wait的連接一直占用著端口沒(méi)釋放,導(dǎo)致端口被占滿(mǎn)(最高的時(shí)候6w+個(gè)),因此HttpClient建立連接的時(shí)候會(huì)出現(xiàn)申請(qǐng)端口沖突的情況。

          具體情況如下:

          time_wait特征

          于是為了解決time_wait的問(wèn)題,網(wǎng)上搜索了些許資料加上自己的思考,于是認(rèn)為可以通過(guò)連接池來(lái)保存tcp連接,減少HttpClient在并發(fā)情況下隨機(jī)打開(kāi)的端口數(shù)量,復(fù)用原來(lái)有效的連接。但是新的問(wèn)題也由連接池的設(shè)置引入了。

          二. 問(wèn)題過(guò)程

          在估算連接池最大連接數(shù)的時(shí)候,參考了業(yè)務(wù)高峰期時(shí)的請(qǐng)求量為1分鐘1.2w pv,接口平響為1.3s(復(fù)雜的廣告推廣效果模擬系統(tǒng),在這種場(chǎng)景平響高是業(yè)務(wù)所需的原因)。

          因此qps為12000*1.3\60=260

          然后通過(guò)觀察了業(yè)務(wù)日志,每次連接建立耗時(shí)1.1s左右, 再留70%+的上浮空間(怕連接數(shù)設(shè)置小出系統(tǒng)故障),最大連接數(shù)估計(jì)為2601.1*1.7約等于500。

          為了減少對(duì)之前業(yè)務(wù)代碼最小的改動(dòng),保證優(yōu)化的快速上線驗(yàn)證,仍然使用的是HttpClient3.1 的MultiThreadedHttpConnectionManager,然后在線下手寫(xiě)了多線程的測(cè)試用例,測(cè)試了下并發(fā)度確實(shí)能比沒(méi)用線程池的時(shí)候更高,然后先在我們的南京機(jī)房小流量上線驗(yàn)證效果,效果也符合預(yù)期之后,就開(kāi)始整個(gè)北京機(jī)房的轉(zhuǎn)全。結(jié)果轉(zhuǎn)全之后就出現(xiàn)了意料之外的系統(tǒng)異常。。。

          三. 案情回顧

          在當(dāng)天晚上流量轉(zhuǎn)全之后,一起情況符合預(yù)期,但是到了第二天早上就看到用戶(hù)群和相關(guān)的運(yùn)維群里有一些人在反饋實(shí)況頁(yè)面打不開(kāi)了。這個(gè)時(shí)候我在路上,讓值班人幫忙先看了下大概的情況,定位到了耗時(shí)最高的部分正是通過(guò)連接池調(diào)用后端服務(wù)的部分,于是可以把這個(gè)突發(fā)問(wèn)題的排查思路大致定在圍繞線程池的故障來(lái)考慮了。

          于是等我到了公司,首先觀察了一下應(yīng)用整體的情況:

          • 監(jiān)控平臺(tái)的業(yè)務(wù)流量表現(xiàn)正常,但是部分機(jī)器的網(wǎng)卡流量略有突增
          • 接口的平響出現(xiàn)了明顯的上升
          • 業(yè)務(wù)日志無(wú)明顯的異常,不是底層服務(wù)超時(shí)的原因,因此平響的原因肯定不是業(yè)務(wù)本身
          • 發(fā)現(xiàn)30個(gè)機(jī)器實(shí)例竟然有9個(gè)出現(xiàn)了掛死的現(xiàn)象,其中6個(gè)北京實(shí)例,3個(gè)南京實(shí)例

          四. 深入排查

          由于發(fā)現(xiàn)了有近 1/3的實(shí)例進(jìn)程崩潰,而業(yè)務(wù)流量沒(méi)變,由于RPC服務(wù)對(duì)provider的流量進(jìn)行負(fù)載均衡,所以引發(fā)單臺(tái)機(jī)器的流量升高,這樣會(huì)導(dǎo)致后面的存活實(shí)例更容易出現(xiàn)崩潰問(wèn)題,于是高優(yōu)看了進(jìn)程掛死的原因。

          由于很可能是修改了HttpClient連接方式為連接池引發(fā)的問(wèn)題,最容易引起變化的肯定是線程和CPU狀態(tài),于是立即排查了線程數(shù)和CPU的狀態(tài)是否正常

          1、CPU狀態(tài)

          CPU特征

          如圖可見(jiàn)Java進(jìn)程占用cpu非常高,是平時(shí)的近10倍

          2、線程數(shù)監(jiān)控狀態(tài):

          圖片
          圖片

          圖中可以看到多個(gè)機(jī)器大概在10點(diǎn)初時(shí),出現(xiàn)了線程數(shù)大量飆升,甚至超出了虛擬化平臺(tái)對(duì)容器的2000線程數(shù)限制(平臺(tái)為了避免機(jī)器上的部分容器線程數(shù)過(guò)高,導(dǎo)致機(jī)器整體夯死而設(shè)置的熔斷保護(hù)),因此實(shí)例是被虛擬化平臺(tái)kill了。之前為什么之前在南京機(jī)房小流量上線的時(shí)候沒(méi)出現(xiàn)線程數(shù)超限的問(wèn)題,應(yīng)該和南京機(jī)房流量較少,只有北京機(jī)房流量的1/3有關(guān)。

          接下來(lái)就是分析線程數(shù)為啥會(huì)快速積累直至超限了。這個(gè)時(shí)候我就在考慮是否是連接池設(shè)置的最大連接數(shù)有問(wèn)題,限制了系統(tǒng)連接線程的并發(fā)度。為了更好的排查問(wèn)題,我回滾了線上一部分的實(shí)例,于是觀察了下線上實(shí)例的 tcp連接情況和回滾之后的連接情況

          回滾之前tcp連接情況:

          圖片

          回滾之后tcp連接情況:

          發(fā)現(xiàn)連接線程的并發(fā)度果然小很多了,這個(gè)時(shí)候要再確認(rèn)一下是否是連接池設(shè)置導(dǎo)致的原因,于是將沒(méi)回滾的機(jī)器進(jìn)行jstack了,對(duì)Java進(jìn)程中分配的子線程進(jìn)行了分析,總于可以確認(rèn)問(wèn)題。

          jstack狀態(tài):

          圖片

          從jstack的日志中可以很容易分析出來(lái),有大量的線程在等待獲取連接池里的連接而進(jìn)行排隊(duì),因此導(dǎo)致了線程堆積,因此平響上升。由于線程堆積越多,系統(tǒng)資源占用越厲害,接口平響也會(huì)因此升高,更加劇了線程的堆積,因此很容易出現(xiàn)惡性循環(huán)而導(dǎo)致線程數(shù)超限。

          那么為什么會(huì)出現(xiàn)并發(fā)度設(shè)置過(guò)小呢?之前已經(jīng)留了70%的上浮空間來(lái)估算并發(fā)度,這里面必定有蹊蹺!

          于是我對(duì)源碼進(jìn)行了解讀分析,發(fā)現(xiàn)了端倪:

          圖片

          如MultiThreadedHttpConnectionManager源碼可見(jiàn),連接池在分配連接時(shí)調(diào)用的doGetConnection方法時(shí),對(duì)能否獲得連接,不僅會(huì)對(duì)我設(shè)置的參數(shù)maxTotalConnections進(jìn)行是否超限校驗(yàn),還會(huì)對(duì)maxHostConnections進(jìn)行是否超限的校驗(yàn)。

          于是我立刻網(wǎng)上搜索了下maxHostConnections的含義:每個(gè)host路由的默認(rèn)最大連接,需要通過(guò)setDefaultMaxConnectionsPerHost來(lái)設(shè)置,否則默認(rèn)值是2

          所以并不是我對(duì)業(yè)務(wù)的最大連接數(shù)計(jì)算失誤,而是因?yàn)椴恢酪O(shè)置DefaultMaxConnectionsPerHost而導(dǎo)致每個(gè)請(qǐng)求的Host并發(fā)連接數(shù)只有2,限制了線程獲取連接的并發(fā)度(所以難怪剛才觀察tcp并發(fā)度的時(shí)候發(fā)現(xiàn)只有2個(gè)連接建立 ?? )

          五. 案情總結(jié)

          到此這次雪崩事件的根本問(wèn)題已徹底定位,讓我們?cè)俅尉珶挼目偨Y(jié)一下這個(gè)案件的全過(guò)程:

          1. 連接池設(shè)置錯(cuò)參數(shù),導(dǎo)致最大連接數(shù)為2
          2. 大量請(qǐng)求線程需要等待連接池釋放連接,出現(xiàn)排隊(duì)堆積
          3. 夯住的線程變多,接口平響升高,占用了更多的系統(tǒng)資源,會(huì)加劇接口的耗時(shí)增加和線程堆積
          4. 最后直至線程超限,實(shí)例被虛擬化平臺(tái)kill
          5. 部分實(shí)例掛死,導(dǎo)致流量轉(zhuǎn)移到其他存活實(shí)例。其他實(shí)例流量壓力變大,容易引發(fā)雪崩

          關(guān)于優(yōu)化方案與如何避免此類(lèi)問(wèn)題再次發(fā)生,我想到的方案有3個(gè):

          1. 在做技術(shù)升級(jí)前,要仔細(xì)熟讀相關(guān)的官方技術(shù)文檔,最好不要遺漏任何細(xì)節(jié)
          2. 可以在網(wǎng)上找其他可靠的開(kāi)源項(xiàng)目,看看別人的優(yōu)秀的項(xiàng)目是怎么使用的。比如github上就可以搜索技術(shù)關(guān)鍵字,找到同樣使用了這個(gè)技術(shù)的開(kāi)源項(xiàng)目。要注意挑選質(zhì)量高的項(xiàng)目進(jìn)行參考
          3. 先在線下壓測(cè),用控制變量法對(duì)比各類(lèi)設(shè)置的不同情況,這樣把所有問(wèn)題在線下提前暴露了,再上線心里就有底了

          以下是我設(shè)計(jì)的一個(gè)壓測(cè)方案:

          a. 測(cè)試不用連接池和使用連接池時(shí),分析整體能承受的qps峰值和線程數(shù)變化

          b. 對(duì)比setDefaultMaxConnectionsPerHost設(shè)置和不設(shè)置時(shí),分析整體能承受的qps峰值和線程數(shù)變化

          c. 對(duì)比調(diào)整setMaxTotalConnections,setDefaultMaxConnectionsPerHost 的閾值,分析整體能承受的qps峰值和線程數(shù)變化

          d. 重點(diǎn)關(guān)注壓測(cè)時(shí)實(shí)例的線程數(shù),cpu利用率,tcp連接數(shù),端口使用情況,內(nèi)存使用率

          綜上所述,一次連接池參數(shù)導(dǎo)致的雪崩問(wèn)題已經(jīng)從分析到定位已全部解決。在技術(shù)改造時(shí)我們應(yīng)該要謹(jǐn)慎對(duì)待升級(jí)的技術(shù)點(diǎn)。

          在出現(xiàn)問(wèn)題后,要重點(diǎn)分析問(wèn)題的特征和規(guī)律,找到共性去揪出根本原因。

          原文鏈接:https://blog.csdn.net/qq_16681169/article/details/94592472

          1. Java8 中的真的 Optional 很強(qiáng)大,你用對(duì)了嗎?

          2. Redis 生產(chǎn)架構(gòu)選型解決方案

          3. Spring Boot 青睞的數(shù)據(jù)庫(kù)連接池HikariCP為什么是史上最快的?

          4. System.currentTimeMillis的性能,真有如此不堪嗎?

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù) Java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 78
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  狼人久久| 精品无码一区二区三区四区久久久 | 国产美女一级毛片真精品酒店 | 多人p| 日韩爱爱爱爱 |