您的位置:首页 > 数据库 > Redis

常驻内存的PHP程序报redis server has gone away

2017-06-20 00:00 1941 查看

常驻内存的PHP程序redis报错问题 redis server has gone away

第一次遇到问题

近半年来公司的业务开始使用redis作为临时数据的存储和简单的消息队列。在这段时间里遇到了不少问题,因为公司的服务是workerman构建的。上redis后,经常半夜三更报redis exception 错误。

在前几次的排查过程中,我们注意到redis 报错,查找资料得知:常驻内存的PHP程序如workerman,因为redis连接长时间不通讯,被redis服务端断开了。当php里再次使用这个连接时就触发了错误。

解决方案

redis服务端如果设置timeout=0,redis服务端并不会主动关闭redis连接。
于是我们修改了redis服务端的timeout=0;这样操作以后,redis竟然没有再报错误.

第二次遇到问题

正当我们以为问题解决的时候,一天晚上,大约凌晨三点十分的样子,redis再次抛出redis server went away的错误。



我们再次开始查找各种资料,焦头烂额之时,从运维那里得知一条重要消息,当天晚上我们服务器所在的阿里云机房发生了网络抖动,已经得到了阿里云技术工程师的确认。



于是,我们开始思考,redis连接会断开的原因,

1,redis服务端主动关闭(已被我们排除)

2,连接异常断开。

第二条正好与当天阿里云发生网络抖动相吻合
于是我们猜测阿里云网络抖动导致php与redis服务端的连接中断。

解决方案

在此猜测下,我们提出了几条解决方案

1、每次使用完redis后,销毁链接。下次使用redis的时候重新初始化链接。

2、在调用redis底层接口时捕获下异常或者判断下错误码,根据异常或者错误码判断链接是否已经断开,如果断开就重新连一次,然后重新执行要执行的查询

3、使用redis连接的时候记录下本次使用连接的时间戳,下次使用的时候当前时间减去间戳等于连接空闲的时间,如果连接空闲的时间过长,则销毁连接,重新建立一个redis链接。

4、搞个定时器,定时发一个空查询给redis服务端,类似心跳的功能,检测redis连接是否存活,不存活就关闭重连。

5、每次从单例模式中拿连接的时候给redis服务端发下ping包,检测次连接是否存活,不存活就关闭重新初始化redis连接。

下面我们来分析下几条解决方案的优点和缺点

1,缺点:每次均存在redis服务器建立TCP连接三次握手及连接关闭四次挥手的开销,不能最大程度利用workerman常驻内存的优势;优点:每次使用时建立连接,不存在redis连接失效的情况

2,缺点:需要重写redis扩展提供的方法,对现有系统改动较大,代码量较大;优点:最大化利用workerman常驻内存的优势,又最好的解决了redis连接失效抛错的问题,确保每次redis请求都能成功

3,缺点:需要记录每次redis请求的时间,需要对现有的redis扩展方法做出修改,又不能完全避免redis连接失效带来的问题。

4,缺点:无法彻底避免redis连接失效带来的问题,如如果在心跳间隙内出现问题,此方法无法处理。

5,缺点:无效请求量较大,每次拿连接都需要发送Ping包,也无法完全避免连接失效的问题,例如从拿连接到下次拿连接的间隙,如果redis连接出现故障,将无法检测到;优点:对现有系统修改最小,也能很好的避免redis连接失效带来的问题

经以上分析,最可靠的方案是第二种

但是按时为了更好的与现有的系统融合,最快解决问题的思想下,我们准备先采用第五种方案,后期逐步过渡到第二种方案.

第二种方案上线后我们会持续观察效果

发现

其实我们发现虽然php的redis扩展底层有断线自动重连机制,但是并不可靠,不然就不存在上述问题了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Redis PHP workerman