详解PHP解决守护进程Redis假死

 更新时间:2021年06月04日 09:19:07   作者:李留广  
公司业务有一个常驻后台运行的守护进程。在这个守护进程当中使用了 Redis List 结构保存业务数据进行队列消费。结果运行过程中,有时候半个月,有时候几个月就会突然不再消费队列里面的数据。我们发现进行心中检测之后,程序的稳定性大大提高。

一、一个简单的守护进程示例

<?php
$redis = new \Redis();
$redis->connect('localhost', 6379);
$redis->auth('xxxxx'); // Redis 密码如果没有设置为空字符串。
$redis->select(1);

$queueKey    = 'redis_queue_services_key';     // 业务数据队列。
$queueIngKey = 'redis_queue_services_ing_key'; // 处理中的队列。

try {
    while (true) {
        $element = $redis->bRPopLPush($queueKey, $queueIngKey, 60);
        if ($element) {
            $data = json_decode($element, true);
            /**
             *
             ...... 此处省略业务逻辑 ......
             *
             */
        } else {
            usleep(100000); // 睡眠 0.1 秒。
        }
    }
} catch (\Exception $e) {
    exit("Error:{$e->getMessage()}");
}

这段代码我们很容易看懂。

它就是通过Redis的阻塞方法bRPopLPush循环从 Redis 队列中取出数据并处理。如果没有取到数据就休眠一秒。之所以休眠是为了保证 CPU 能得到充分的利用。因为,我们已经使用了阻塞方法阻塞 60 秒。所以,这个位置休眠与否并不重要。

当我们的业务出现任何错误,我们通过try catch进行异常捕获然后将错误信息直接输出并退当前脚本。

博主寒冰第一次编写常驻后台运行的守护进程时,就是如上这种方式写的代码。结果,这段代码运行到 30s 的时候报错了。提示我们 socket 流超时。于是我在这个脚本头部加了如下代码:

ini_set('default_socket_timeout', -1);

这样我们的PHP就不会主动段掉我们与 Redis 的 socket 连接了。

但是,好景不长。过了一段时间,大概半个月吧。运维同学告诉我 Redis 队列的数据出现了未消费的情况。然后,我查看了消费日志。的确没有产生新的消费日志。因为我有一个习惯,每个消费消费的时候都会把成功消费的日志写到文件中。消费失败的也写入日志文件中。这样,我就知道失败的具体原因。

但是,这次我真的没有发现有任何的错误发生。

  • 常驻后台进程处理存活状态。并没有变成孤儿进程。
  • 常驻后台进程内存也没有出现泄漏。
  • 系统 CPU/内存 资源都处理正在状态。
  • 系统打开的句柄资源也是低消状态。
  • 带宽也处理低消状态。
  • 其它常驻进程也处理正常消费的工作状态。也就排除了 Redis 故障的问题。

我当时也怀疑过是不是像MySQL一样常时间连接不进行任何操作,服务器端会主动断开连接。但是,MySQL 服务器端主动段掉连接会提示:MySQL server has gone away的错误。但是,我们的 Redis 服务器端没有给我们报任何错误信息呀。

我们公司用的是阿里云的 Redis 产品。我怀疑是不是 Redis 版本太低造成的这个隐性 BUG。于是,我们将阿里云的 Redis 服务升级到了阿里云支持的最新版本。

结果还是失败了。我们的 Redis 还是假死了。或者说我们的 Redis 处于伪活状态。

你认为 Redis 活着,其实它早已经死了。你认为 Redis 死了,但是它却没有死亡的特征。

最后,我冷静下来。

我假定此时的 Redis 已经死了。只是没有告诉客户端而已。那么我只需要每次检测一下 Redis 连接是否存活就好了。

于是,我翻看了 Redis 的 API。发现它提供了一个ping()的方法来检测连接是否存活。

二、一个不再假死(伪活)的 Redis 常驻进程示例

<?php

$redis = new \Redis();
$redis->connect('localhost', 6379);
$redis->auth('xxxxx'); // Redis 密码如果没有设置为空字符串。
$redis->select(1);

$queueKey    = 'redis_queue_services_key';     // 业务数据队列。
$queueIngKey = 'redis_queue_services_ing_key'; // 处理中的队列。

try {
    while (true) {
        $element = $redis->bRPopLPush($queueKey, $queueIngKey, 60);
        if ($element) {
            $data = json_decode($element, true);
            /**
             *
             ...... 此处省略业务逻辑 ......
             *
             */
        } else {
            $pong = $redis->ping();
            if ($pong != '+PONG') {
                throw new \Exception('Redis ping failure!', 500);
            }
            usleep(100000); // 睡眠 0.1 秒。
        }
    }
} catch (\Exception $e) {
    exit("Error:{$e->getMessage()}");
}

通过代码对比,我们在第一版代码的基础上加了如下代码:

$pong = $redis->ping();
if ($pong != '+PONG') {
    throw new \Exception('Redis ping failure!', 500);
}

我们向 Redis 服务器发送ping的时候,服务器会返回+PONG字符串。当然,这个是 Redis 扩展封装过的方法。真正的 ping 是不会有 + 号的。

当我们每次 ping 的时候,Redis 服务器就会认为我们的 Redis 客户端连接处于存活状态。就不会断掉我们的连接了。

把代码进行改造之后,假死头痛的问题再也没出现了。

以上就是详解PHP解决守护进程Redis假死的详细内容,更多关于PHP解决守护进程Redis假死的资料请关注脚本之家其它相关文章!

相关文章

  • php 抽象类的简单应用

    php 抽象类的简单应用

    我想博客中的 文章列表和单个的文章阅读 统一起来,我觉得除了sql查询语句结构不同,HTML代码不同,其它也就一样。不过话说回来,这两个都是主要功能,所以这的确有点不适合,不过昨晚读了一点设计模式,好歹得写点啥好。
    2011-09-09
  • PHP和Mysql中转UTF8编码问题汇总

    PHP和Mysql中转UTF8编码问题汇总

    这篇文章主要总结了PHP页面和Mysql数据库的转UTF8编码问题,需要的朋友可以参考下
    2015-10-10
  • php生成酷炫的四个字符验证码

    php生成酷炫的四个字符验证码

    可以生成随机颜色、干扰线条元素、干扰点元素的php验证码,这篇文章主要介绍了php生成酷炫的四个字符验证码的编写方法,感兴趣的小伙伴们可以参考一下
    2016-04-04
  • PHP 配置open_basedir 让各虚拟站点独立运行

    PHP 配置open_basedir 让各虚拟站点独立运行

    好几年前,我在抱怨Apache运行PHP的安全性不行,只要一个站点被人拿下,服务器上的其他站点就会跟着遭殃。
    2009-11-11
  • PHP调用外部程序的方法解析

    PHP调用外部程序的方法解析

    这篇文章主要介绍了PHP调用外部程序的方法解析,对此感兴趣的同学,可以多研究一下
    2021-04-04
  • pdo中使用参数化查询sql

    pdo中使用参数化查询sql

    在使用参数化查询的情况下,数据库服务器不会将参数的内容视为SQL指令的一部份来处理,而是在数据库完成 SQL 指令的编译后,才套用参数运行,因此就算参数中含有具破坏性的指令,也不会被数据库所运行。
    2011-08-08
  • PHP+JS三级菜单联动菜单实现方法

    PHP+JS三级菜单联动菜单实现方法

    这篇文章主要介绍了PHP+JS三级菜单联动菜单实现方法,涉及JavaScript动态控制页面样式及PHP数据库操作相关技巧,需要的朋友可以参考下
    2016-02-02
  • 基于PHP实现的事件机制实例分析

    基于PHP实现的事件机制实例分析

    这篇文章主要介绍了基于PHP实现的事件机制,实例分析了事件机制的原理及php中debug_backtrace函数完成事件机制的实现技巧,需要的朋友可以参考下
    2015-06-06
  • PHP Laravel框架缓存操作介绍

    PHP Laravel框架缓存操作介绍

    缓存是使Web应用程序快速运行的重要工具。这很容易实现,但很难做到正确。如何使用Laravel的内置系统实现缓存,该系统支持Memcached和Redis等多种后端
    2022-09-09
  • php实现的后台表格分页功能示例

    php实现的后台表格分页功能示例

    这篇文章主要介绍了php实现的后台表格分页功能,涉及php针对数据库的连接、查询、删除、动态生成表格等相关操作技巧,需要的朋友可以参考下
    2017-10-10

最新评论