按照官網(wǎng)配置redis,運行一段時間后出現(xiàn)鏈接斷開問題,
Next RedisException: Connection closed in /abc/vendor/illuminate/redis/Connections/PhpRedisConnection.php:406
出現(xiàn)此問題,我測試會在兩種情況下出現(xiàn)
<?php
#default_socket_timeout為默認(rèn)值60
#第一種
$rds = new \Redis();
try {
$ret = $rds->connect("127.0.0.1", 6379);
if ($ret == false) {
echo "Connect return false";
exit;
}
//設(shè)置超時時間為 0.1ms
$rds->setOption(Redis::OPT_READ_TIMEOUT, 0.0001);
var_dump($rds->get("user_count"));
} catch (Exception $e) {
var_dump($e);
}
#第二種(運行以下代碼,去手動殺掉redis鏈接)
$rds = new Redis();
try {
$ret = $rds->pconnect("127.0.0.1", 6379);
if ($ret == false) {
echo "Connect return false";
exit;
}
while (1) {
var_dump($rds->get("aa"));
}
} catch (Exception $e) {
var_dump($e);
}
第一種情況我們可以使用設(shè)置$rds->setOption(Redis::OPT_READ_TIMEOUT, -1); 不超時
或者修改default_socket_timeout為-1
但是第二種情況當(dāng)網(wǎng)絡(luò)出現(xiàn)閃斷的時候就避免不了,最好加一個短線重連。
我看illuminate/redis 7.X 并沒有短線重連功能,并且在配置文件里面 read_timeout 設(shè)置為 -1 會報錯,因為它的 $client->setOption(Redis::OPT_READ_TIMEOUT, $config['read_timeout']);
參考這個文檔給redis加一個定時心跳試下。
http://m.wtbis.cn/doc/webman#/db/heartbeat
redis擴展會自動重連。你的問題應(yīng)該是連接被某個網(wǎng)絡(luò)節(jié)點清理了,沒有通知客戶端(沒有發(fā)fin包),客戶端認(rèn)為連接仍然存活,繼續(xù)使用導(dǎo)致出錯。比如用了阿里云的redis或者mysql服務(wù),redis連接和mysql連接都會因為長時間不活躍被偷偷清理(不通知客戶端那種),客戶端不知道連接斷開就不會重連,導(dǎo)致讀寫超時。解決辦法就是定時加心跳,心跳間隔小于60秒。
這是我模擬網(wǎng)絡(luò)差的情況。網(wǎng)絡(luò)中斷一次,那么如果webman 依賴redis的服務(wù)就不可用了,除非重啟,我們開發(fā)的時候redis和webman用的一臺機器,所以沒有發(fā)現(xiàn)過此問題,但是我生產(chǎn)環(huán)境 redis 和webman 用的內(nèi)網(wǎng)不同服務(wù)器,就會有此問題。
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://m.wtbis.cn/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace support\bootstrap;
use support\exception\BusinessException;
use Webman\Bootstrap;
use Illuminate\Redis\RedisManager;
class Redis implements Bootstrap
{
/**
* @var RedisManager
*/
protected static $_manager = null;
/**
* @param \Workerman\Worker $worker
* @return void
*/
public static function start($worker)
{
if (!class_exists('\Illuminate\Redis\RedisManager')) return;
$config = config('redis');
static::$_manager = new RedisManager('', 'phpredis', $config);
self::connection()->setOption(\Redis::OPT_READ_TIMEOUT, -1); #這里設(shè)置為讀取不超時
}
/**
* @param string $name
* @return \Illuminate\Redis\Connections\Connection
*/
public static function connection($name = 'default')
{
return static::$_manager->connection($name);
}
/**
* @param $name
* @param $arguments
* @return mixed
*/
public static function __callStatic($name, $arguments)
{
#這里修改為斷線后重新創(chuàng)建對象
try {
return static::$_manager->connection('default')->{$name}(... $arguments);
} catch (\RedisException $ex) {
self::start(null);
log_write('redis 重新創(chuàng)建對象');
return static::$_manager->connection('default')->{$name}(... $arguments);
}
}
}
如果遇到網(wǎng)絡(luò)閃斷,或者redis掛掉后恢復(fù)了,webman操作redis 任然報錯問題。
這是我的解決方案,大家以后遇到可以參考