1. 程式人生 > >高併發下PHP請求Redis異常處理

高併發下PHP請求Redis異常處理

最近發現線上伺服器經常報連線redis異常:Uncaught exception 'RedisException' with message 'Redis server went away'
於是摘下一臺線上機,對伺服器一半以上的介面進行壓測:

$ http_load -p 100 -f 100000 urls.txt 
100000 fetches, 100 max parallel, 2.46403e+09 bytes, in 46.6896 seconds
24640.3 mean bytes/connection
2141.81 fetches/sec, 5.27747e+07 bytes/sec
msecs/connect: 1.04977
mean, 3.755 max, 0.859 min msecs/first-response: 44.0343 mean, 142.5 max, 11.804 min HTTP response codes: code 200 -- 100000 $ http_load -p 100 -s 100 urls.txt 145919 fetches, 100 max parallel, 3.65654e+09 bytes, in 100 seconds 25058.7 mean bytes/connection 1459.19 fetches/sec, 3.65654e+07 bytes/sec msecs/connect: 1.10872
mean, 14.339 max, 0.863 min msecs/first-response: 65.4696 mean, 1012.95 max, 12.124 min 11082 bad byte counts HTTP response codes: code 200 -- 134837 code 500 -- 11082

發現當介面的請求量達到10w的時候,QPS可以達到2000以上;請求量超過10w的時候,QPS會降到1500以下,需要訪問redis服務的介面會出現Redis server went away異常,並返回500

難道說現在機器負載的瓶頸就是redis了嗎?壓力測試時的redis伺服器壓力大麼?

這裡寫圖片描述
這裡寫圖片描述

從監控的趨勢圖可以看到,壓測介面請求的redis最高QPS也就20000,客戶端連線數只有不到80,壓力並不大。所以瓶頸不會是在redis伺服器上。

在排除了Linux,php,nginx,redis配置方面可能出現的問題後,最後鎖定問題,應該是在連線Redis的時候出現異常。那PHP是如何連線redis的呢?使用redis擴充套件。而問題很有可能出在php的redis擴充套件上,就是phpredis

通常我們在使用phpredis連線redis的時候,都是用的connect()方法,在大量的php請求請求到redis的時候,這種方式會不斷的建立和關閉連線,佔用很多資源,導致伺服器出現大量的TIME_WAIT,這樣就會有很多php請求無法連線redis,出現Redis server went away這個異常,實際就說明redis服務沒有收到請求,所以昨天看到在壓測過程中redis負載壓力並不大,但php還是返回500
之前的思路都是以為redis層面問題或者伺服器配置甚至硬體問題,在換個思路之後,發現phpredis還有個連線redis的方法,就是pconnect(),這個方法很多人都知道,但是很少把它和高併發聯絡到一起,pconnect()connect()的區別網上有很多,總結一下就是pconnect方法建立後的連線並不隨這請求的結束而關閉,而是依賴於php-fpm程序,php-fpm程序不死,redis connect就一直存在,直到空閒超時自動斷開(目前redis timeout 600),這樣就極大的提高了redis connect的重複利用,減少大量的IO開銷。不過這樣也就出現一個瑕疵,就是redis的connect clients數量要比以前高出很多,目前線上機單臺伺服器php-fpm程序300,線上伺服器共有12臺,單臺redis最高connect clients可以達到3600,不過這也遠低於單臺redis配置的maxclients 200000

所以我在之前的機器上做了修改,然後重新壓測:

$ http_load -p 100 -s 100 urls.txt 
253884 fetches, 100 max parallel, 6.44571e+09 bytes, in 100 seconds
25388.4 mean bytes/connection
2538.84 fetches/sec, 6.4457e+07 bytes/sec
msecs/connect: 1.07964 mean, 13.918 max, 0.858 min
msecs/first-response: 36.6679 mean, 250.973 max, 7.66 min
1 bad byte counts
HTTP response codes:
  code 200 -- 253884

可以看到100個程序跑了100s,請求量253884,QPS達到2538.84,這個數字基本正常。
再看一下redis負載情況:
這裡寫圖片描述
這裡寫圖片描述
重新上線了三臺機器之後,可以看到redis端connect_clients明顯增加,但是QPS還是很平穩(請忽略壓測導致的凸起點)。

最後有必要反思一下這個過程,其實最後解決問題的方法是有點狗血的,因為pconnect方法很多人都知道,但是都沒有把這個方法和高併發請求redis聯絡起來,所以我嚴重懷疑很多人對這個方法理解的並不深刻。在遇到問題時,一定要敢於嘗試,如果你發現這個問題Google上都沒有答案的時候,你應該覺得這是個挑戰,所以不要輕易放棄。還有眼界要廣,不要糾結於一個點,像redis這麼成熟的軟體系統,十萬級的訪問量基本不會構成威脅,所以要檢查一個請求經過的每一個環節。

這裡寫圖片描述