目前项目对Redis依赖较重,使用phpredis扩展操作Redis, 但频繁出现Redis server went away错误。
常见的网络连接错误原因:
Network is unreachable |
到目标ip无可用路由 非常罕见(断网,或没有网关时) |
connection timedout |
tcp建立连接超时(目标主机不可达到,或产生丢包) iptables在高并发连接时丢包,可能导致连接超时 ip_conntrack: table full, dropping packet |
connection refused |
连接被拒绝,目标主机存活,端口未开放 |
Couldn't resolve host |
域名解析失败 |
但是phpredis的server went away非常让人困惑,为了弄清楚phpredis产生server went away的原因,有必要深入分析,从而才有助于解决这个问题。
这个错误信息与PDO的MySQL Server has gone away非常类型,但是词法上却有细微差别
一、准备知识
MySQL Server has gone away产生的原因和解决办法?
pdo使用gone, 而phpredis使用went, 为此查阅资料,两者区别如下:
gone 用于现在完成时, 表示“去了” 强调已经完成
went 用于一般过去式, 表示“去”. 不强调是否已经完成
1.He has gone to Shanghai.他已经去了上海(强调已经到达).
2.He went to Shanghai yesterday.他昨天去了上海(有可能还未到达 还在路上).
所以:
MySQL Server has gone away MySQL服务器已经走了
Redis Server went away Redis Server走了
虽然两者语义上理解上都没有问题,但是学究一些,gone away比went away更为准确一些。
二、<!--[endif]-->案例分析:
目前生产环境使用的phpredis版本为2.2.7, 以此版本为准进行调试。
1. 无法连接时, 假设本机并没有Redis server监听4512端口,以下代码的运行结果?
<?php
$redis = new Redis();
$redis->connect('localhost', 4512);
常规方式,连接失败就应该抛出异常,但是phpredis在这里不会抛出异常,也不会产生任何报错,只会返回false, 所以拦截connect异常是徒劳的。
结论:phpredis连接失败时,不抛出异常也不产生错误信息,只返回false。
2. 连接失败 又同时进行了操作
<?php
$redis = new Redis();
$redis->connect('localhost', 4512);
$redis->set('name', 'value');
这种情况下,如果连接失败,就会抛出went away异常, 但是从语义上来说went away其实是错误的。
3. 未连接就进行操作(虽然这样没有意义)
<?php
$redis = new Redis();
$redis->set('name', 'value');
这种情况下,也会抛出went away异常。同样,这其实也存在语义上的问题,不曾拥有,何谈失去?
总结:phpredis在连接失败时,不会抛出异常,只返回false。操作时,如果没有有效的连接,才抛出异常。
4. Redis Server的错误消息响应
根据Redis的协议,Redis一共有5种消息类型,使用特定前缀字符区分:
https://redis.io/topics/protocol
Simple Strings: +
Errors: -
Integers: :
Bulk: $
Arrays: *
错误响应时的消息样式:
-Error message\r\n
本机启动redis server于端口6379, 继续测试:
<?php
$redis = new Redis();
$redis->connect('localhost', 6379);
$redis->select(18); //默认数据库编号从0~15, 这里故意模拟错误
结果:只是返回false, 没有任何错误或异常产生!
通过strace调试,实际上Redis Server会输出错误: -ERR DB index is out of range\r\n
但是phpredis并没有抛出异常或错误,导致很难定位错误原因。
再比如,对一个没有启用密码保护的Redis Server, 尝试密码登录:
<?php
$redis = new Redis();
$redis->connect('localhost', 6379);
这种情况下,仍然不会产生异常或是错误信息,只会返回false, 实际上Redis Server会返回: -ERR Client sent AUTH, but no password is set, 这个可以用strace调试得出。
phpredis对错误响应消息的抑制,导致问题排查困难。
二、phpredis源码分析:
1. 在phpredis源码中搜索went away,发现以redis.c源程序的int redis_sock_get函数中有went away异常抛出
PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) {
zval **socket; int resource_type;
if (Z_TYPE_P(id) != IS_OBJECT || zend_hash_find(Z_OBJPROP_P(id), "socket", sizeof("socket"), (void **) &socket) == FAILURE) { /* Throw an exception unless we've been requested not to */ if(!no_throw) { zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC); } return -1; }
*redis_sock = (RedisSock *) zend_list_find(Z_LVAL_PP(socket), &resource_type);
if (!*redis_sock || resource_type != le_redis_sock) { /* Throw an exception unless we've been requested not to */ if(!no_throw) { zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC); } return -1; } if ((*redis_sock)->lazy_connect) { (*redis_sock)->lazy_connect = 0; if (redis_sock_server_open(*redis_sock, 1 TSRMLS_CC) < 0) { return -1; } }
return Z_LVAL_PP(socket); } |
大概的意思就是:
查询是否有socket资源,如果没有抛出went away异常。
查询出了资源,但是资源无效(如连接失败),抛出went away异常。
继续跟踪connect方法的流程:
PHP_METHOD(Redis, connect) { if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0) == FAILURE) { RETURN_FALSE; } else { RETURN_TRUE; } } |
PHP_METHOD(Redis, connect)定义Redis类的connect方法,调用流程:
redis_connect -> redis_sock_get -> redis_sock_server_open(定义在library.c), 关键代码:
redis_sock->stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, tv_ptr, NULL, &errstr, &err );
|
也就是说,连接失败时,这里并不会抛出任何异常, 只是返回-1, errstr, err已经包含了详细的错误信息和错误码。
修改源码,连接失败时输出详细的错误信息:
原代码: efree(host); if (!redis_sock->stream) { efree(errstr); return -1; } |
修改后的代码 if (!redis_sock->stream) { char* message = emalloc(256); //分配256字节用于保存错误信息 sprintf(message, "%s %s", host, errstr); //格式化字符串 主机地址 错误信息 错误码 zend_throw_exception(redis_exception_ce, message, err TSRMLS_CC); //抛出异常 efree(errstr); efree(host); efree(message); return -1; } efree(host); |
然后重新编译phpredis,连接失败就会抛出异常:
PHP Fatal error: Uncaught exception 'RedisException' with message 'localhost:6379 Connection refused' in /root/redis.php:4 |
替代的方案:
使用其它成熟的redis扩展或类, yii2官方提供了一个类,yii2-redis, 经过研究这个类代码成熟,可以替代phpredis, 特点:
1. 使用php代码编写,调用底层socket实现连接、请求发送、响应解析,无须安装redis客户端扩展,也不依赖其它第三方扩展,也不受机器环境配置的影响。
2. 利于编程者更容易掌握Redis的协议,容易扩展。
3. 提供更直观实用的socket编程范例。
性能上可能比phpredis稍低,但这个差别是微乎其微的,可以忽略。
相关推荐
1. 下载Redis:首先,你需要从Redis官方网站或可靠的第三方源下载Redis的Windows 64位版本,其中包含`redis-server.exe`、`redis-cli.exe`等核心组件。 2. 配置文件:解压下载的压缩包,找到`redis.windows.conf`...
在本文中,我们将深入探讨Redis在`rails默认缓存`、`自定义缓存`以及`任务队列`中的应用,并结合`redis-server`的下载与配置,提供全面的知识讲解。 首先,让我们谈谈Redis在Rails(Ruby on Rails框架)中的默认...
redis-stack-server-7.2.0-v9.arm64.snap redis-stack-server-7.2.0-v9.bionic.arm64.tar.gz redis-stack-server-7.2.0-v9.bionic.x86_64.tar.gz redis-stack-server-7.2.0-v9.bullseye.x86_64.tar.gz redis-stack-...
1.redisServer(服务程序包含 window32和 window64两个版本) 2RedisStudio客户端下载 3. redis-desktop-manager下载 4. RedisClient源码 5.RedisClient.jar运行jar下载
windows下的redis安装包 自动添加path 环境 可结合文文章给php安装reidos扩展https://blog.csdn.net/linyunping/article/details/79737746
使用`redis-server.exe --service-install`命令,指定配置文件路径,如`redis-server.exe --service-install redis.windows.conf`。然后通过`--service-start`和`--service-stop`命令控制服务的启停。 4. **使用...
标题“redis-server on windows”表明我们将讨论如何在Windows操作系统上安装和运行Redis服务器。 尽管Redis官方主要推荐在Linux环境下运行,因为它的性能和稳定性在Linux上更佳,但有时出于开发或测试的需求,我们...
redis-server 下载,在linux下运行。由redis 3.2.8 解压后编译而成
redis-server.exe
`redis-server`是Redis服务端程序,负责处理客户端的请求并管理数据库。在这个绿色版中,解压后可以直接运行此可执行文件启动Redis服务。配置文件通常为`redis.conf`,你可以通过修改配置文件来定制Redis的行为,...
Redis是什么?程序员都很清楚,不多介绍。开源代码仅支持Linux,windows平台的自6.0后,很多是基于msys或cygwin编译出来的,非真正的windows下的服务程序。本发行包基于原版代码,保持绝大部分功能的一致性,并且能以...
windows操作系统中使用的redis服务包。 不需要安装,解压之后放到非系统盘,然后使用命令行模式 进入目录 执行 redis-server.exe redis.windows.conf 可以按照配置文件启动redis服务。
Redis Stack 是一个全面的数据平台,它包含了 Redis 数据库本身以及一系列相关工具,旨在提供更完整、更高效的数据管理和分析解决方案。这里的 "redis-stack-server-6.2.6-v7.rhel7.x86-64.tar.gz" 文件是一个针对 ...
叉编译好的ARM 板 redis-server 5.0,可直接放板上linux/andriod跑 编译对高手不难,但对于小白来讲还是挺麻烦的,一个小错误就会导致中断。今天我帮你编译好了,拿去吧。 运行: 放到系统中: chmod +x redis-...
一键启动/关闭redis
Redis-server是Redis的主要服务器进程,它负责处理客户端的连接请求,执行命令并管理数据。在Windows中,`redis-server.exe`是这个进程的可执行文件。它根据配置文件(如`redis.windows.conf`)启动并管理Redis实例...
5. `redis-server.exe`:这是Redis服务器的可执行文件,负责处理客户端请求和管理数据存储。 6. `redis-cli.exe`:Redis命令行界面工具,用于与Redis服务器交互,执行命令如读写数据、查看键空间、执行事务等。 7. `...
此外,了解如何通过`redis-cli`或`redis.bat`进行故障排查和日志分析也是运维Redis的重要技能。 总之,Redis在Windows上的运行涉及到下载合适的二进制文件、配置`redis.windows.conf`、使用`redis.bat`脚本启动服务...
redis合集(深入了解redis,redis实践,redis适用场景及实现,redis学习笔记整理,redis命令参考手册,python简明教程.chm,redis_cmd.chm)