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

目录
  • 一、一个简单的守护进程示例
  • 二、一个不再假死(伪活)的 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假死的资料请关注我们其它相关文章!

(0)

相关推荐

  • PHP操作Redis常用命令的实例详解

    redis常用命令有: 1.连接操作命令: 2.持久化命令: 3.远程服务控制命令: 4.对value操作命令:5.string命令: 6.list命令: 7.set命令: 8.hash命令等等. Redis 常用命令 登录 redis-cli -p 5566 -a password 检查key是否存在 EXISTS key 搜索某关键字 KSYS *4 返回一个Key所影响的vsl的类型 TYPE key 下面通过代码看下PHP操作Redis命令,代码如下所示: //连接本地的 Redis 服

  • php基于redis的分布式锁实例详解

    在使用分布式锁进行互斥资源访问时候,我们很多方案是采用redis的实现. 固然,redis的单节点锁在极端情况也是有问题的,假设你的业务允许偶尔的失效,使用单节点的redis锁方案就足够了,简单而且效率高. redis锁失效的情况: 客户端1从master节点获取了锁 master宕机了,存储锁的key还没来得及同步到slave节点上 slave升级为master 客户端2从新的master上获取到同一个资源的锁 于是,客户端1和客户端2同事持有了同一个资源的锁,锁的安全性被打破. 如果我们不考

  • Thinkphp5+Redis实现商品秒杀代码实例讲解

    环境:wamp,redis 要求:安装WAMP,Redis,以及为PHP安装Redis扩展 秒杀功能大致思路:获取缓存列表的长度,如果长度(llen)等于0,就停止秒杀,即秒杀失败,如果长度大于0,则继续运行,先从缓存中移除一个元素(lpop),再进行数据库操作(添加订单表,商品库存数量减一),如果再进一个人秒杀,就再走一遍流程,循环往复. 一.安装Redis扩展 1.查看PHP版本信息 打开phpinfo.php,查看PHP版本,我的是PHP7.3.4,还有一个需要注意Architecture

  • PHP使用Redis队列执行定时任务实例讲解

    Redis类: <?php namespace Utils; use Phalcon\Config\Adapter\Ini as ConfigIni; class Redis{ private static $redis1; private static $session; /** * 获取一个单例的redis对象 * @param string $name * @return \Redis */ public static function getObj($name='redis1') { t

  • php在linux环境中如何使用redis详解

    1.php安装. 2.下载redis并编译(最好是在 /usr/local目录下运行该命令) # wget http://download.redis.io/releases/redis-6.0.8.tar.gz # tar xzf redis-6.0.8.tar.gz # cd redis-6.0.8 # make 3.完成后进入src目录运行redis # cd src # ./redis-server 如果运行成功,会出现redis图片,以及必要信息提示成功. 服务器规则组打开6379端口

  • php之redis短线重连案例讲解

    php redis断线重连,pconnect连接失败问题 介绍 在swoole ,workerman等cli长连接模式下,遇到Redis异常断开,后面又开启的情况,一般得重新启动程序才能正常使用, 本文介绍在不重启服务,实现原来的Redis断线重连 原理 Redis 断开的情况下调用 $Redis->ping()会触发Notice错误,Notice: Redis::ping(): send of 14 bytes failed with errno=10054 当获取redis实例时,如果pin

  • php操作redis命令及代码实例大全

    官方PHP Redis扩展文件下载 https://pecl.php.net/package/redis 选择与你PHP版本相应的文件下载后直接放到PHP目录下的ext文件夹里,然后修改php.ini配置文件如下 php.ini文件添加:extension=php_redis.dll 重启php, phpinfo可以看到redis则证明安装成功 php连接redis测试 <?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379)

  • thinkphp5redis缓存新增方法实例讲解

    找到该文件 thinkphp/library/think/cache/driver/Redis.php 进行新增方法 在这里 我就举例几个 如何添加 添加的方法查看 redis教程 /** * 返回列表中指定区间内的元素 * */ public function lrange($key,$start,$end){ return $this->handler->lrange($key,$start,$end); } /** * 在list左边新增元素 * */ public function l

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

    目录 一.一个简单的守护进程示例 二.一个不再假死(伪活)的 Redis 常驻进程示例 一.一个简单的守护进程示例 <?php $redis = new \Redis(); $redis->connect('localhost', 6379); $redis->auth('xxxxx'); // Redis 密码如果没有设置为空字符串. $redis->select(1); $queueKey = 'redis_queue_services_key'; // 业务数据队列. $qu

  • linux 守护进程详解及建立守护进程

    linux 守护进程详解及建立守护进程 守护进程是一种后台运行并且独立于所有终端控制之外的进程. 守护进程的启动 要启动一个守护进程,可以采取一下几种方式: 在系统期间通过系统的初始化脚本启动守护进程.这些脚本通常在目录etc/rc.d下,通过它们所启动的守护进程具有超级用户的权限.系统的一些基本服务通常都是通过这种方式启动的. 很多网络服务程序都是由inetd守护程序启动的.它监听各种网络请求,如telnet.ftp等,在请求到达时启动相应的服务器程序(telnet server,ftp se

  • PHP实现多进程并行操作的详解(可做守护进程)

    如下所示: 复制代码 代码如下: /** * 入口函数 * 将此文件保存为 ProcessOpera.php * 在terminal中运行 /usr/local/php/bin/php ProcessOpera.php & * 查看进程 ps aux|grep php */ProcessOpera("runCode", array(), 8); /** * run Code */function runCode($opt = array()) {   //需要在守护进程中运行的

  • 详解Python中的GIL(全局解释器锁)详解及解决GIL的几种方案

    先看一道GIL面试题: 描述Python GIL的概念, 以及它对python多线程的影响?编写一个多线程抓取网页的程序,并阐明多线程抓取程序是否可比单线程性能有提升,并解释原因. GIL:又叫全局解释器锁,每个线程在执行的过程中都需要先获取GIL,保证同一时刻只有一个线程在运行,目的是解决多线程同时竞争程序中的全局变量而出现的线程安全问题.它并不是python语言的特性,仅仅是由于历史的原因在CPython解释器中难以移除,因为python语言运行环境大部分默认在CPython解释器中. 通过

  • 详解SpringBoot2.0的@Cacheable(Redis)缓存失效时间解决方案

    问题   @Cacheable注解不支持配置过期时间,所有需要通过配置CacheManneg来配置默认的过期时间和针对每个类或者是方法进行缓存失效时间配置. 解决   可以采用如下的配置信息来解决的设置失效时间问题 配置信息 @Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { return new RedisCacheManager( RedisCacheWriter.no

  • 详解Python中的进程和线程

    进程是什么? 进程就是一个程序在一个数据集上的一次动态执行过程.进程一般由程序.数据集.进程控制块三部分组成.我们编写的程序用来描述进程要完成哪些功能以及如何完成:数据集则是程序在执行过程中所需要使用的资源:进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志. 线程是什么? 线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID.程序计数器.寄存器集合和堆栈共同组成.线程的引入减小了程序并发

  • Python中str is not callable问题详解及解决办法

    Python中str is not callable问题详解及解决办法 问题提出: 在Python的代码,在运行过程中,碰到了一个错误信息: python代码: def check_province_code(province, country): num = len(province) while num <3: province = ''.join([str(0),province]) num = num +1 return country + province 运行的错误信息: check

  • 实例详解Spring Boot实战之Redis缓存登录验证码

    本章简单介绍redis的配置及使用方法,本文示例代码在前面代码的基础上进行修改添加,实现了使用redis进行缓存验证码,以及校验验证码的过程. 1.添加依赖库(添加redis库,以及第三方的验证码库) <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency&

  • 详解闭包解决jQuery中AJAX的外部变量问题

    详解闭包解决jQuery中AJAX的外部变量问题 在AJAX中,我们经常都要使用外部变量,经常会多次使用,如下代码 function getCarInfo(){ for(var i=0;i<4;i++){ var carId = $("#carList0"+i+" #carId").val(); var request = { city: city, carId: carId }; $.ajax({ url:"enquiry", type:

  • 微信小程序 合法域名校验出错详解及解决办法

    微信小程序 合法域名校验出错详解及解决办法 看下出现错误: 微信小程序开发,遇到这个问题怎么办? 产生原因是因为发起了wx.request请求 而请求的域名没有在微信公众平台后台设置. 1.开发者自己手动设置不检验 不管你有没有拿到App ID ,我相信很多人都会和我一样做一件事,就是二话不说先大概了解下文档,然后直接上github找项目,导入试试.我是这么做的,结果上来就报这个错,后来发现其实可以自己设置本地开发工具不校验域名,完全没有影响. 在"项目">最后一个选项,勾选之后

随机推荐