幹工第一天,這個api超時優化把我幹趴下了!

近日我司進行雲服務商更換,恰逢由我負責新上線的三方調用 api 維護管理,在將服務由阿里雲部署到騰訊雲過程中,我們壓測發現在騰訊雲調用京東接口時 TP999 抖動十分劇烈,儘管業務層有重試操作但是超時依然較多,並不滿足業務要求…… 接下來針對過程中發現的種種問題我們便踏上了優化之路。

開端

那還是普通的一夜,突然羣裏蹦出一些報警消息,多條業務線調用外部 api 超時,dubbo 線程池數量不足,還好很快服務恢復了正常,不過這也爲我提了個大醒,我維護的服務作爲業務調用的聚合出口,一旦服務發生異常將會導致很多業務方失敗。儘管在壓測過程中我們便知道了遷移雲後的 api 調用可用性會有明顯下降,但在 C 端業務還未遷移時便出了這麼大影響確實是前期準備不足。

基於表面問題分析

出現問題先從最容易下手的地方開始解決,先分析問題,線程池飆高報警,結合日誌、APM(skywalking) 工具進行排查,發現報警時刻線程數量和 cpu 均出現飆升情況,由於服務主要提供的功能就是三方調用,很多線程卡在調用三方 api 等待響應結果步驟上,並且有響應結果的調用消耗在 http 的時間也特別長,這就很容易理解了,是由於三方 api 抖動加之服務創建 http 連接過多先是導致應用響應緩慢,再之服務超時觸發重試瞬間導致應用負載翻倍,進而加劇了服務緩慢問題。在不具備限流降級能力時服務一旦出現這種情況很容易導致雪崩,萬幸沒有太多業務損失,作爲一個成熟的程序員總不能給自己留坑啊,趕緊來填坑!

多方位填坑

問題找到了,方案自然不缺了

  1. 修改 http 調用方式,引入 http 連接池

​ 這個方案其實在之前壓測過程中便考慮採用了,由於使用的三方 sdk 在 http 調用中均使用 java 原生的 HttpURLConnection ,這在接口高併發情況下不停創建連接,性能實在太差了。考慮團隊內大家熟悉程度直接用 HttpClinet 覆蓋三方 SDK 中的 HttpUtil 實現,稍加修改即可。修改好重新進行壓測,TP999 指標、cpu 使用率均有明顯下降,這波漂亮,投入產出 ++!

  1. 第一個方案就萬事大吉了嗎?支持請求超時分級,線程隔離

​ 在第一個方案開發上線後我們又發現了新問題,業務側超時情況依然明顯!這個原因很容易想到,針對不同業務方我們只配置了一份超時配置,儘管部分業務方(主要是 C 端業務,對接口超時十分敏感)會提前超時然後觸發重試,但是我的應用線程依然在阻塞請求,這是資源的浪費啊,仔細想想,之前的問題還是沒有解決,在客戶端不停重試下服務的 dubbo 線程豈不是還得爆了啊。再優化,這次我想到了請求分級,由於不同業務方對接口的響應時效要求不同,我們進行了連接池隔離,針對不同的業務方調用可以在調用時設置請求分級,我們大致分類下創建了三種分級 ‘FAST’,'STANDARD','SLOW',不同分級由服務提供者去配置使用不同超時方案。服務穩定性又進一步,問題再一次得到了解決。

這裏還要提醒下大家 HttpClinet 使用過程中一般會配置連接池使用,切記搭配連接池使用時超時參數要配置三個,分別爲

ConnectionRequestTimeout 獲取連接池連接的超時時間(這個大家最容易忽略了)
ConnectTimeout 連接超時時間
SocketTimeout socket 讀取超時時間
  1. 合理應用重試(消費者重試)

    儘管在提供者服務上已進行代碼優化,但是爲了提升業務成功率,合理的使用重試也是很有必要的。此處要注意如果服務響應較慢千萬避免消費者的多級重試,如果我們的整個業務調用鏈每一層都做了重試那麼就會導致鏈路中響應慢的服務壓力愈發增長,嚴重的引發重試風暴,直接壓垮服務,所以合理設置重試也是很關鍵的一環,這裏我們後續也要考慮引入熔斷降級方案,避免意外發生。

在上述三招連環出擊後,問題基本告一段落,2C4G 200 dubbo 線程,30 http 線程,吞吐量固定 1k,服務的 TP999 在壓測過程中基本可以穩定在 500 毫秒以下,滿足各方要求。

問題再起

伴隨着應用接入量越來越大,我們的 C 端業務方又給提出了一個新問題,服務的超時情況怎麼和阿里的服務差那麼大啊(此處僅是指京東接口),由於是新業務直接上線到騰訊雲,懷疑可能是我們服務的性能問題,這個在之前只有一些 B 端業務我確實沒有過多注意,必須把這個問題好好查一查,還是從 APM 工具側查看,果然,接口的 TP999 抖動十分劇烈,很多達到超時閾值,而 C 端業務十分重視這些指標,這也值得我們去學習,往往這些指標上線很容易暴露出根本問題,在高併發下看 TP90、TP99 時指標看起來還是挺正常的,那是因爲超高請求量將響應時間給平均下去了。

糾結求解

這次的問題不再簡單,面對這些 TP999 的超時響應,我們在觀測了應用的整體流量、JVM 情況等等,愣是沒有找到瓶頸,有之前阿里雲的應用對比我們堅信問題是存在的,可能是在網絡請求層面。接下來我們一步步出擊。

0、查看 APM 工具發現部分 HTTP 請求確實超時,還有一部分是 HTTP 請求耗時不算長,但是從 APM 統計的調用鏈路來看耗費在提供者服務的時間比較長導致超時了。上述爲兩個問題,分別來看,請求可以確定是超時的我懷疑是不是由於使用了 http 連接池,池中的鏈接是不是超時了,從 httpclient 的日誌中我分析了一下並沒有這個問題,因爲三方響應的 header 只返回了 keepalived,並沒有返回其有超時時間。第二個問題我反覆查找當時並沒有找到原因,服務壓力不大,也沒有發生長耗時的 GC,實在不好解釋這個問題,但是後來問題找到了,繼續看結果在下文。

1、爲了觀察應用的請求響應信息,我們對 http 出口進行了抓包,通過對大量請求的抓包分析,我們找到了在響應比較高的時候抓包的 ip 中竟然有香港的 ip,爲了驗證這個問題,我們去百度了 dns 解析,發現該 ip 確實爲該域名所有。

圖上部分爲此域名解析出的香港 ip,下半部分爲該域名解析出的北京 ip,同一臺機器上響應時長差距明顯
圖上部分爲此域名解析出的香港 ip,下半部分爲該域名解析出的北京 ip,同一臺機器上響應時長差距明顯

2、我們的服務器和出口 ip 都是北京的爲什麼 dns 解析出來的 ip 會返回香港的呢?帶着問題我們求助了騰訊雲官方,在官方問題排查過程中,我們又做了其他測試,在服務器上寫了一個 python 小腳本定時進行指定域名的 dns 域名解析,果然在跑了一段時間後出現了幾次解析結果爲香港的 ip,狂喜。此時騰訊雲官方也有了迴應,由於京東自己搭建的 DNS 解析服務器,但是無法準確識別騰訊雲的 ip 地域,導致偶爾會解析到香港,解決方案是他們推動京東去識別騰訊雲的 ip。

該圖爲騰訊雲默認 dns 統計腳本的日誌信息,第三個 ip 地址爲香港,雖然較少出現,但是訪問耗時長,且不定時會丟包
該圖爲騰訊雲默認 dns 統計腳本的日誌信息,第三個 ip 地址爲香港,雖然較少出現,但是訪問耗時長,且不定時會丟包

3、等官方解決進度遲緩啊,我們能有什麼臨時的解決辦法嗎?糾結之時我們也接入了架構組爲我們搭建的 kong 出口網關,有專門的 net 出口,更高的帶寬,完善的 granfa 監控,結合監控請求抖時展現更明顯了,於是我們想到了固定 host,更改 dns 方案,由於固定 host 風險太大了,我們採用了更改 dns 的方案嘗試。原來機器上默認是騰訊雲的 dns,我們分別更換爲阿里的 dns 和 114 的 dns 進行了測試,測試結果是差距不大最終我們採用了阿里的 dns,在更換阿里的 dns 後效果明顯,原來 TP999 的尖刺明顯減少,觀察 kong 日誌也不存在香港的 ip 了,這裏就有點詫異騰訊不是說和 dns 沒關係嗎,爲什麼換了別人家的 dns 情況就好了很多(這個問題的結果是明確的,但是原因目前還是停留在猜測階段,猜測是 114 和阿里 dns 對此有優化,所以在該場景下優於騰訊 dns,若大家有方案可以驗證此問題歡迎提出來哦)。

dns 切換後依據 kong 統計計算的 SLA 信息,會比較明顯看出效果
dns 切換後依據 kong 統計計算的 SLA 信息,會比較明顯看出效果

小插曲,kong 網關的 dns 解析器是寫在 kong.conf 配置文件中的,如果未配置該項則會讀取系統的 /etc/resolv.conf 文件,每次 kong 啓動後會讀取配置並緩存到內存中,如果啓動 kong 後再修改系統的 nameserver 對 kong 是不管用的!還好我們之前有在專門的機器中測試 dns 更換效果,所以纔敢肯定是 kong 配置問題,但是來回也折騰了不少時間才找到問題。

在這裏我也想告訴大家,和別人協作,如果可以請一定準備好對比數據,避免他人因質疑而不配合,或者合作結果並不如預期時可以有個對照。

4、記得最開始有一個問題當時沒找到原因嗎,‘一部分 HTTP 請求耗時不算長,但是從 APM 統計的調用鏈路來看耗費在提供者服務的時間比較長導致超時了。’ 這個的問題最後還是 http 請求超時的原因,我對照了 kong 的日誌和服務的 httpClient 日誌,發現出現這種問題的請求有個共同點就是請求頭很快接收到了,但是 body 遲遲未讀取到然後觸發超時了。skyWaling 中展示的 HTTP 耗時很短的原因是因爲只要開始有響應就算請求結束了,然後爲什麼會有隻返回 header 的情況呢?我 ping 了一下出現問題的 ip 是香港的,響應時間明顯要比北京的慢,而且偶爾會丟包。這個問題解釋通了。

終是踏平

經過又一輪的問題解決,測試加驗證效果耗費了近兩週時間,服務的可用性也提升了一個層次,算是一個小坑,不過這也讓我們對 http 的整個請求流程做了很多梳理和二次認知,以上便是我在解決這個超時問題的整個解決路徑和心路歷程,感謝看我的叨叨叨,希望我們可以一起共同進步。

更多精品文章,歡迎大家掃碼關注我的公衆號「碼海」

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章