代碼運行一段時間后,會報下面的錯誤。
[Predis\Connection\ConnectionException]
Error while reading line from the server. [tcp://127.0.0.1:6379]
最初的懷疑是連接數過多,導致連接不上服務器,出現上述錯誤。查看進程,發現大量redis狀態為TIME_WAIT的tcp連接。
首先考慮的是,減少TIME_WAIT的進程,保持隨時可以連接到服務器。所以想到的減少TIME_WAIT狀態的進程,將進程快速回收。修改內核參數sysctl.conf,net.ipv4.tcp_timestamps=1(1為開啟),開啟快速回收net.ipv4.tcp_tw_recycle=1。tw_recycle是通過時間戳判斷哪個是最新的進程,將不是最新的TIME_WAIT的進程回收,所以需要先開啟tcp_timestamps。
修改后觀察,果然沒有繼續報錯。
但是使能快速回收TIME_WAIT進程,可能會丟包,導致沒有收到應答,不能成功建立連接。但這種辦法也不是最佳解決辦法,尤其修改內核參數,涉及環節太多,需深入了解才可修改。
底層不去修改,就從predis客戶端入手,源碼發現有read_write_timeout這個參數,可以設置超時時間,這樣讀取流數據時就不會報錯。Predis作者建議設置關閉redis.conf中timeout(修改timeout 0),表示不關閉與客戶端的連接,我感覺這樣比較耗費資源,可以適當增加timeout時間。
所以這次暫時是這樣解決的,設置read_write_timeout=-1和redis.conf的timeout參數。
其實有很多解決方式,后續可以繼續尋找一個更優的方案。
比較了常見的兩個php連接redis客戶端,phpredis和predis。Laravel中使用的predis,其中連接redis使用connect,當請求結束連接關閉。而phpredis使用pconnect連接,依賴於php-fpm,php-fpm不關閉,連接一直都在。再次使用pconnect,連接會被重用,不會再次新建。
后來查看predis源碼發現,persistent這個參數,手冊說明都沒有提到,但是看字面意思,可能類似於pconnect方式的持久連接,這個后續再研究一下。
