详解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关键字仅替换一次的实现函数

    这篇文章主要介绍了php实现每个关键字仅需要替换一次,有时一个项目里面涉及到批量替换关键字的问题,本文针对控制替换次数进行研究,感兴趣的小伙伴们可以参考一下
    2015-10-10
  • PHP5.6读写excel表格文件操作示例

    PHP5.6读写excel表格文件操作示例

    这篇文章主要介绍了PHP5.6读写excel表格文件操作,结合实例形式分析了php5.6环境下使用PHPExcel插件针对Excel表格读写的相关操作技巧,需要的朋友可以参考下
    2019-02-02
  • 11个PHP 分页脚本推荐

    11个PHP 分页脚本推荐

    Web开发中,分页设计必不可少。本文列举了10个PHP分页脚本,希望对你的web开发会有帮助。 列表中大部分,提供演示和代码下载。
    2011-08-08
  • PHP设计模式之抽象工厂模式实例分析

    PHP设计模式之抽象工厂模式实例分析

    这篇文章主要介绍了PHP设计模式之抽象工厂模式,结合实例形式分析了php抽象工厂模式的概念、原理、使用方法及相关操作注意事项,需要的朋友可以参考下
    2019-03-03
  • php unicode编码和字符串互转的方法

    php unicode编码和字符串互转的方法

    下面小编就为大家带来一篇php unicode编码和字符串互转的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • php mysql获取表字段名称和字段信息的三种方法

    php mysql获取表字段名称和字段信息的三种方法

    本文章向大家介绍php获取mysql表字段名称和字段信息的三种方法,分别为:mysql_fetch_field方法、desc方法和SHOW FULL FIELDS方法,
    2016-11-11
  • php常用Stream函数集介绍

    php常用Stream函数集介绍

    本篇文章是对php中的常用Stream函数集进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • php获取url参数方法总结

    php获取url参数方法总结

    这篇文章主要介绍了php获取url参数方法,实例总结了利用parse_url()函数解析URL的方法,需要的朋友可以参考下
    2014-11-11
  • 解析PayPal支付接口的PHP开发方式

    解析PayPal支付接口的PHP开发方式

    PayPal 快速、安全而又方便,是跨国交易的首选在线付款方式。现在PayPal支付接口可以和国内大部分信用卡关联,可以实现国人的跨国交易收支。
    2010-11-11
  • 详解Laravel服务容器的优势

    详解Laravel服务容器的优势

    如果说laravel框架的核心是什么,那么无疑是服务容器。理解服务容器的概念,对于我们使用laravel太重要了,应该说是否理解服务容器的概念是区分是否入门laravel的重要条件。因为整个框架正是在服务容器这一基础上构建起来的。
    2021-05-05

最新评论