首頁  >  文章  >  後端開發  >  php+redis在實際專案中HTTP 500: Internal Server Error故障排除的方法

php+redis在實際專案中HTTP 500: Internal Server Error故障排除的方法

墨辰丷
墨辰丷原創
2018-05-26 15:17:051819瀏覽

用戶量快速增長,訪問量在短時間內翻倍,由於前期容量規劃做得比較好,硬體資源可以支撐,可是軟體系統方面出現了大問題:40% 的請求都會返回HTTP 500: Internal Server Error

問題描述
用戶量快速成長,訪問量在短時間內翻倍,由於前期容量規劃做得比較好,硬體資源可以支撐,可是軟體系統方面出現了大問題:
40% 的請求都會回傳HTTP 500: Internal Server Error
透過檢視日誌,發現錯誤是在PHP e09be6022d700e04aeaa85a5f42fdcb2 Redis 的連線處理上
偵錯處理

第1次
剛開始時並沒有找到根本原因,只能嘗試各種與錯誤相關的辦法,例如:
增加PHP 連接數,並把逾時時間從500ms 增加到2.5s
禁止掉PHP 設定中的default_socket_timeout
在主機系統中禁止掉SYN cookies
檢查Redis 和Webservers 的檔案描述符數量
增加主機系統的mbuffer
調整TCP backlog 數量

嘗試了很多方法,但全部無效

第2次
想在預發布環境中重現這個問題,可惜,還是沒成功,應為流量不夠大,無法復現

第3次
會不會是程式碼中沒有關閉Redis連接呢?
正常來講,PHP在執行結束時會自動關閉資源連接,但舊版中會有記憶體洩漏的問題,保險起見,把程式碼都修改一遍,手動關閉連接
結果還是無效

第4次
懷疑目標:phpredis 這個客戶端函式庫
做A/B 測試,替換回predis 這個函式庫,部署到資料中心中20% 的使用者量上
得益於好的程式碼結構,替換工作很快完成
可結果依舊是無效,但也有好的一面,可以證明phpredis 沒問題嘛

##第5次查看了一下Redis 的版​​本,是v2.6,當時最新版本是v2.8.9
升級Redis 試一下吧,升完後還是不行
沒事,要保持樂觀,這不順便把Redis 版本升到最新的了

第6次透過查找大量文檔,在官方文檔中發現了一個調試好方法Redis Software Watchdog,打開後執行:

$ redis-cli --latency -p 6380 -h 1.2.3.4
min: 0, max: 463, avg: 2.03 (19443 samples)

查看Redis 日誌:

#

...
[20398] 22 May 09:20:55.351 * 10000 changes in 60 seconds. Saving...
[20398] 22 May 09:20:55.759 * Background saving started by pid 41941
[41941] 22 May 09:22:48.197 * DB saved on disk
[20398] 22 May 09:22:49.321 * Background saving terminated with success
[20398] 22 May 09:25:23.299 * 10000 changes in 60 seconds. Saving...
[20398] 22 May 09:25:23.644 * Background saving started by pid 42027
...

##發現了問題:

每隔幾分鐘就向硬碟保存一次數據,fork 一個後台存儲進行為什麼需要大概400ms(透過上面日誌的第1條和第2條的時間可以看出來)


到這兒,終於找到問題的根源了,因為Redis 實例中有大量的數據,導致每次持久化操作fork 後台進程時非常耗時,並且在他們的業務中經常修改key,又導致了頻繁觸發持久化,也就經常產生對Redis 的阻塞

處理辦法:使用單獨的slave 來做持久化

這個slave 不處理真實的流量請求,唯一的作用就是處理持久化,把之前Redis 實例上的持久化操作轉移到這個slave 上

效果非常明顯,問題基本上解決,但有的時候還是會報錯

##第7次

排查可能阻塞Redis 的慢查詢,發現有地方使用了keys *因為Redis 中的資料越來越多,這個指令自然會產生嚴重阻塞

可以使用scan 來取代

第8次

經過前面的調整,問題已經解決,隨後的幾個月,即使流量在不斷增長,也都抗住了
#但他們意識到了新的問題:

現在的方式是,來一個請求就創建一個Redis 連接,執行幾個命令,然後再斷開連接,在請求量很大時,這個方式產生了嚴重的性能浪費,一半以上的命令是用來處理連接操作的,這都超過了業務邏輯上的處理,也使Redis 變慢

解決方法:引入proxy,他們選擇了twitter 的twemproxy,只需要在每個webserver上安裝代理,twemproxy負責與Redis 實例進行持久連接,這樣就大大減少了連接方面的操作

twemproxy還有兩個方便的地方:

支援memcached

#可以阻止非常耗時或危險的指令,例如keys、flushall

效果自然很完美,再也不用擔心先前的連線錯誤



第9次

#透過資料分片來繼續優化:
對不同上下文的資料拆分隔離
對相同上下文的資料進行一致性雜湊分片

效果:


減少了每台機器上的請求、負載
提升了快取的可靠性,不擔心節點故障

以上就是本文的全部內容,希望對大家的學習有幫助。

相關推薦:

PHP取得

redis

裡不存在的6位元隨機數的方#

PHP實作redis訊息佇列發布微博的方法

CI框架(CodeIgniter)操作redis步驟解析

以上是php+redis在實際專案中HTTP 500: Internal Server Error故障排除的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn