出现该问题的原因是poll设置接收超时所致,这个超时默认设置60s
设置Redis::OPT_READ_TIMEOUT配置项:
解决方法如下:
<?php
$redis = new Redis();
$redis->pconnect('127.0.0.1', '6379');
$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);
$redis->subscribe(['channel-name'], function($instance,$channelName, $message) {
echo $channelName, "==>", $message, PHP_EOL;
});
?>
问题描述
redis提供了pub/sub功能,但在使用phpredis的subscribe时发现这样一个问题,代码如下(sub.php):
<?php
function process($redis, $chan, $msg){
var_dump($msg);
}
$redis = new Redis();
$res = $redis->connect('127.0.0.1', '7979');
$redis->subscribe(array('demo'), 'process');
代码运行后,发现如果在一段时间内未收到来自demo频道的消息,则会报如下错误:
PHP Fatal error: Uncaught exception 'RedisException' with message 'read error on connection' in sub.php:11
Stack trace:
thrown in sub.php on line 11
原因分析
为了查找原因,我们使用strace对代码进行了跟踪:
strace php sub.php
截取部分重要输出如下:
//连接redis
connect(3, {sa_family=AF_INET, sin_port=htons(7979), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
//发送subscribe命令
sendto(3, "subscribe demo\r\n", 17, MSG_DONTWAIT, NULL, 0) = 17
//收到响应
recvfrom(3, "*3\r\n$9\r\nsubscribe\r\n$4\r\ndemo\r\n:1\r"..., 8192, MSG_DONTWAIT, NULL, NULL) = 33
poll([{fd=3, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
//套机字超时时间设为60s
poll([{fd=3, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 0 (Timeout)
//等待超时,关闭连接
close(3) = 0
//输出错误信息
write(2, "PHP Fatal error: Uncaught excep"..., 261PHP Fatal error: Uncaught exception 'RedisException' with message 'read error on connection'
可见报错的本质是poll设置接收超时所致,从starce结果我们知道这个超时默认是60s。
解决
我们有两种方法改变超时
- 方法1
在代码起始处设置
ini_set('default_socket_timeout', -1);
$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);
两种方法中的-1均表示永不超时,你也可以将超时设置为自己希望的时间。
无论使用哪种方法,再次strace, 你会发现poll的超时被设为了-1。
recvfrom(3, "*3\r\n$9\r\nsubscribe\r\n$4\r\ndemo\r\n:1\r"..., 8192, 0, NULL, NULL) = 33
poll([{fd=3, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
//超时被设为-1, 即永不超时
poll([{fd=3, events=POLLIN|POLLERR|POLLHUP}], 1, -1
个人比较推荐方法2,它只影响到redis本身。而方法1会对其它方法产生影响,比如
file_get_contents等。
总结
使用phpredis的subscribe时,默认60内没有收到消息,sub端就会因超时异常退出。可以自行设置延长超时时间或永不超时。
来自 https://blog.csdn.net/qmhball/article/details/52575133
最近一个后台常驻job通过redis的brpop阻塞读取消息时,设置的超时时间较长
list($key,$row)=$redis->brPop($queue_name,3600); //超时时间为1小时
但是在实际的使用中发现很短时间后就会退出,通过查看error log,发现:'RedisException' with message 'read error on connection' 提示
经过一番折腾,原来发现是php.ini文件中的一个配置项导致:
default_socket_timeout = 60
由于redis扩展也是基于php 的socket方式实现,因此该参数值同样会起作用。
找到了问题就比较好解决了:
1、直接修改php.ini,将其设置为我们想要的值(这个不推荐)
2、在我们的脚本中通过以下方式设置,这样就比较灵活,不对其他脚本产生影响
ini_set('default_socket_timeout', -1); //不超时
来自 https://www.cnblogs.com/xiazh/archive/2012/11/14/2770297.html
来自 https://blog.csdn.net/yongcto/article/details/16947387
来自 https://blog.csdn.net/u010832551/article/details/50485536