去年聖誕節當天,忽然收到一個我經手過的項目的告警郵件,錯誤消息顯示「Redis::CommandError: ERR max number of clients reached」。redis
什麼狀況?難道這個項目翻車了?第一反應是這臺服務器運行着自建的 Redis 數據庫,可是客戶端只有同個內網的一個 Ruby on Rails 的應用,怎麼會有鏈接數爆掉的可能?數據庫
老衲掐指一算:安全
redis-store
gem 源碼,默認鏈接池大小應該是 5,10個 unicorn 工做進程,按需鏈接,最大值是 10 x 5 = 50。在不考慮其餘可能還用到 Redis 鏈接的狀況下,目前已知的最大 Redis 鏈接數需求是 122,這個數遠小於 Redis 理論最大鏈接數啊,並且當時顯示鏈接數到達上萬!並且這個項目已經不多訪問,壓力極其小,不大可能會達到理論所需鏈接數啊!服務器
必定是有某種神祕力量在主導這一切!!!網絡
以上理論最大鏈接數分析只是定性分析,只能大概說明有一些詭異的東西存在,而想真正確認問題根源,還得作定量分析,只有數據才能說明一切!ide
事不宜遲,要採集數據,第一步就是加監控,因此當時就緊急寫了一個定時採集 Redis 客戶端數量(使用 redis 內建 CLIENT LIST
命令)的腳本,結合 crontab 定時運行,將結果寫入文件,做爲後續分析的基礎。spa
經過監控腳本,發現幾個有意思的現象:code
在有了上一步的發現以後,我繼續用系統命令 sudo netstat -apnt
檢查 6379
端口鏈接數發現,客戶端機器也才只有 42 個左右的鏈接到 redis 服務器端,結合最開始的理論鏈接數分析,這個數量是比較合理的。server
可是!可是!反過來去服務端機器用一樣的命令檢查,在服務端視角,卻有多達300+個客戶端創建的鏈接,並且都是在 ESTABLISHED 狀態!這個數量和上面另外一種監控方式獲得的數量一致!進程
究竟是什麼狀況?還能有這種操做?
至此,Redis 鏈接數泄露是板上釘釘的事情了,但是又是爲何呢?爲此,我在網上搜索了不少問答跟文章,後來總算找到了答案,果不其然,仍是默認配置的問題。
redis 爲了不客戶端鏈接數過多,有一個timeout配置,意思是若是鏈接的空閒時間超過了timeout的值,則關閉鏈接。默認配置是0,意思是沒有超時限制,永遠不關閉鏈接。生產上顯然不會配置0……
![]()
OMG!趕忙打開咱們的 redis 的配置文件驗證是否如此,果不其然,redis一直保持着默認配置!
至此,很好解釋爲何鏈接數會泄露了,由於有不少空閒或者實際上客戶端已經斷開的鏈接,在服務器端一側仍然保持着。那什麼狀況會致使這樣的狀況發生呢?
我猜想:
找到問題根源以後,修復起來就簡直太簡單了。事實上,開發領域就是如此,絕大部分時間都花在了找 bug 上,而改掉bug,可能只須要一分鐘不到。
首先,修改了下 redis 數據庫配置:
成功重啓 redis 以後,從新運行前面的監控腳本,以便觀察修復後狀況,初步能夠確認這下服務器端和客戶端的鏈接數一致了:
再又通過幾天的腳本自動採集數據後分析,系統又恢復平穩運行了,鏈接數一直穩定在理論最大鏈接數之下。
這個問題的根源其實很小,可是排查過程仍是花了挺多時間,主要是須要等待採集到足夠的數據後用於分析。其餘心得體會:
0.0.0.0
來源請求的安全漏洞;