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

admin3年前PHP教程23
目录
一、一个简单的守护进程示例二、一个不再假死(伪活)的 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 {
            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 redis的scan用法实例分析

在删除缓存的时候,我们在一些场景下需要批量删除,但不确定具体的key值,可通过匹配的方式进行查询后删除。但是使用keys会导致redis服务器宕机。慎用。。。一般公司也会禁用keys等比较敏感的命令的...

在PHP中操作文件的扩展属性

在操作系统的文件中,还存在着一种我们可以自己定义的文件属性。这些属性不是保存在文件内容中,也不是直接可以通过 ls -al 所能看到的内容。它们可以将一个键值对信息永久得关联到文件上,一般现在的 Li...

浅谈减少Hyperf框架的扫描时间

目录原因解决办法写在最后原因Hyperf框架为了防止用户更新组件后,代理缓存没有更新导致启动报错。增加了以下钩子。{    "scripts"...

如何选择成都gpu服务器主机

如何选择成都gpu服务器主机?GPU服务器,又叫做显卡服务器(高性能处理器+大内存+高性能显卡=GPU服务器)。GPU服务器,简单来说,GPU服务器是基于GPU的应用于视频编解码、深度学习、科学计算等...

为什么选择扬州BGP高防服务器

为什么选择扬州BGP高防服务器?选择扬州BGP高防服务器的原因可能有以下几点:1.位置优势:扬州是位于江苏省中部,交通便利的城市,距离上海等经济中心城市比较近,具有较好的区位优势。2.稳定性:扬州BG...

gpu运算服务器有哪些?租用香港GPU服务器配置和报价?

当下是人工智能的时代,而这一行业对计算性能、能耗、延迟等功效的要求是极高的,而传统的服务器计算平台已经负载,在日益革新的人工智能领域,已经显得力不从心。所以,将目标放在gpu运算服务器上面,以发挥更大...