一個TCP連接池的自我修養-如mysql-client、http-client、redis-client

問題:單服單天過億請求中,出現0.0001%的500怎麼回事?

  • 日誌中不定時出現 mysql gone awayconnect timeout, reset by beer等錯,原因不明。
  • access-log同樣不定時出現5xx錯。
  • 感覺MySQL運行正常,沒壓力,沒慢查,sql使用主鍵查詢–不知道問題在哪?
  • 連接MySQL的代碼使用keepalive。

這個時候,你是否懷疑過是連接庫的問題?

一個TCP連接池的自我修養

  • keepalive特性,idle-connection可設定。
  • one retry 特性。
  • max-connection 設定。
  • read/write timeout 設定。

max-connection 特性是必需的

如果是mysql連接庫,max-connection不設上限,要不就是造成後端的mysql連接過多,拖跨mysql。
如果是http連接庫,就會造成服務應用大量併發請求在等待,不論阻塞、非阻塞同樣會。即使像go語言,這樣原生性非阻塞、輕併發語言。大量的等等請求,一樣會有耗盡內存的風險。換是python、php一個線程就以M爲單位內存計算的,併發更低。
所以必需帶有合理的 setMaxConnection() 接口。

非常重要的容錯

當連接池滿載時,一個新線程請求一個新連接時,採用什麼策略?
等待?
還是直接返回try-later?

read/write timeout 也是必需的

任何的TCP連接,都可能出現的讀/寫超時,對上層來說就是出錯了,如果timeout很長,即直接阻塞了此線程或微線程。大量阻塞同樣地引發內存風險,也不存在語言上的差別。所以,合理的timeout值,特別是read timeout,經常地出現一種,對方服務器處理慢,不能及時返回結果,而client端斷開後,對方繼續完成了工作,而client方以爲沒完成,而重發請求,如果沒防重入,會造成數據不一致。 爲什麼重發?參考one-retry特性說明。

keepalive+ idle-connection 作爲可選特性

追求高性能,TCP的keepalive,連接重用是必需的。
而idle-connection作爲閒時保持最低的連接數量,當上層業務要使用時馬上可用。
還有一種做法是 connection-keep-timeout, 即每個連接閒下來後,多長時間後自動關閉。
MySQL中就可以使用 set timeout=n; 顯式設置每個連接的timeout值,否則使用服務端默認統一值。

one retry 特性,在keepalive打開時,必需加上

one-retry特性,並不是每個程序員都注意並知道的一個常規特性。
而此特性正是解決 我們項目中 0.0001% 5xx 問題 的關鍵。
一旦開啓idle-connection特性後, TCP客端端會保持一定數量在連接在連接池中,上層要使用時返回一個連接。
而像MySQL的通信協議中是有Ping指令的,就是用於測試連接是否有效。所以當返回一個連接時,如果是MySQL,就可以用Ping測試一下,確保返回給上層的是可用連接。
原因是連接池中的連接不一定有效,即使你代碼中監聽了socket的close事件,但由於服務端可能使用了LVS這樣的虛擬IP,很可能出現服務端單方面斷開了TCP連接,客戶端是沒法感知(注:這裏並不是說使用LVS就會有這種現象,只能用於舉例說明,服務端可能使用了客戶端無法感知的連接層)。除此以後,TCP斷開時4次握手不完全,也是很正常的現象。
爲了上層能獲得一個可用連接池,沒有ping協議的HTTP協議怎麼辦?
可以使用one-retry特性:
上層發送一個請求調用,由 連接池 執行, 如出現 reset by beermysql was gone away 情況的,可以新建一次連接,重發一次請求 。我把這種行爲稱爲one-retry, 上層不必感知。若重試的請求,也出來同樣的錯誤,就應該向上層返回或異常了–視語言而定。

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