PHP使用Redis长连接的方法详解


Posted in PHP onFebruary 12, 2018

本文实例讲述了PHP使用Redis长连接的方法。分享给大家供大家参考,具体如下:

php-redis在github上的项目地址:https://github.com/phpredis/phpredis

pconnect函数声明

其中time_out表示客户端闲置多少秒后,就断开连接。函数连接成功返回true,失败返回false:

pconnect(host, port, time_out, persistent_id, retry_interval)
    host: string. can be a host, or the path to a unix domain socket
    port: int, optional
    timeout: float, value in seconds (optional, default is 0 meaning unlimited)
    persistent_id: string. identity for the requested persistent connection
    retry_interval: int, value in milliseconds (optional)

下面的例子详细介绍了pconnect连接的重用情况。

$redis->pconnect('127.0.0.1', 6379);
$redis->pconnect('127.0.0.1'); // 默认端口6379,跟上面的例子使用相同的连接。
$redis->pconnect('127.0.0.1', 6379, 2.5); // 设置了2.5秒的过期时间。将是不同于上面的新连接
$redis->pconnect('127.0.0.1', 6379, 2.5, 'x'); //设置了持久连接的id,将是不同于上面的新连接
$redis->pconnect('/tmp/redis.sock'); // unix domain socket - would be another connection than the four before.

pconnect使用介绍

对pconnect方法简单描述

使用该方法创建连接,连接不会在调用close方法之后关闭,只有在进程结束之后该连接才会被关闭。

[待验证]如果使用的是长连接,Redis配置文件中的timeout配置项需要设置为0,否则连接池中的连接会因为超时而失效

针对PHP-FPM来说明一下pconnect

长连接只会在PHP-FPM进程结束之后结束,连接的生命周期就是PHP-FPM进程的生命周期。
相比较短连接而言,在每一个PHP-FPM调用过程中都会产生一个redis的连接,在服务器上的表性形式就是过多的time_out连接状态。
而长连接相反,PHP-FPM调用的所有CGI都只会共用一个长连接,所以也就是只会产生固定数量的time_out。

关闭长连接

可以调用close和unset方法,但两则差异很大:

- close的作用仅仅是使当前PHP进程不能再进行redis请求,但无法真正关闭redis长连接,连接在后续请求中仍然会被重用,直FPM进程生命周期结束。所以close 并不会销毁redis对象,只是断开连接而已。

- unset 变量才会销毁。也需要注意并不是使用了 pconnect 就不要 close 了,如果当前脚本执行时间很长 那么也会一直占用一个连接的。

如何判断当前Redis是否处于连接状态

等效的问题是,在单例模式中,判断当前实例是否有效。

习惯上调用echo,判断是否正常返回字符串本身,或者调用ping,查看返回值是否为 +PONG。

但是需要特别小心的是,在redis断开连接之后,调用echo以及ping(返回'+POMG')时,均会抛出异常。所以要通过异常捕获机制来处理。

代码分析pconnect连接重用的问题

情况一:非单例模式。

说明:a实例和b实例共用了一条连接,b实例将a实例的连接修改了:
所以下面的例子导致最终$a实例得到的值变成了2,需要特别注意。

$a = pconnect(host, port, time_out);
select(3);
$a -> setex(id, 3);
echo $a -> get(id);
//之后执行下面的连接
$b = pconnect(host, port, time_out);
select(2);
$b->set(id,2)
echo $a->get(id);  //这个id操作的db变成了2,不再是之前的3了。因为这两个连接共用了一个连接通道。

情况二:单例模式。

将上述的代码修改,a和b都通过getInstance来生成。生成的前提是判断当前实例是否存在。单例模式的混淆点在于:

$a生成了一个实例,这时候生成$b, $b使用了$a的实例,然后修改了$a的连接,之后调用$a肯定是调用的$b修改之后的实例。跟情况二一致。
单例模式的代码如下:

public static function getInstance($db = 0)
{
  if (!isset(self::$_instance)) {
    self::$_instance = new Redis();
  }
  self::_connect();
  self::$_instance->select($db);
  return self::$_instance;
}

两种情况都说明了连接重用的问题。如何修复这个bug?两点:

1.为每一个db生成一个单例。
2.避免连接重用问题。

所以代码可以做调整为返回一个单例数组:

public static function getInstance($db = 0)
{
  try{
    if (isset(self::$_instance[$db]) && self::$_instance[$db]->Ping() == 'Pong') {
      return self::$_instance[$db];
    }
  } catch (Exception $e) {
  }
  self::$_instance[$db] = new Redis();
  self::_connect($db);
  return self::$_instance[$db];
}

需要注意的地方

避免在Task类成员变量中使用redis对象。

在redis的单例模式中,声明了time_out的过期时间。如果redis处理的场合是一个任务,而任务调用redis间隔时间又比较长。当间隔大于time_out时候,redis就会断开连接,这时候所有对redis的操作都会失效。解决的办法就是避免这种调用方式,通过在调用的地方动态声明redis类来执行。这种问题对于长连接和短链接是没有区分,属于调用的方式错误。

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
如何对PHP程序中的常见漏洞进行攻击(下)
Oct 09 PHP
基于mysql的bbs设计(五)
Oct 09 PHP
mysql5详细安装教程
Jan 15 PHP
php 用sock技术发送邮件的函数
Jul 21 PHP
PHP的SQL注入过程分析
Jan 06 PHP
php调用C代码的实现方法
Mar 11 PHP
C#使用PHP服务端的Web Service通信实例
Apr 08 PHP
PHP、Python和Javascript的装饰器模式对比
Feb 03 PHP
Joomla调用系统自带编辑器的实现方法
May 05 PHP
10个值得深思的PHP面试题
Nov 14 PHP
ThinkPHP删除栏目(实现批量删除栏目)
Jun 21 PHP
ThinkPHP3.2.3框架实现的空模块、空控制器、空操作,跳转到错误404页面图文详解
Apr 03 PHP
PHP长连接实现与使用方法详解
Feb 11 #PHP
搜索附近的人PHP实现代码
Feb 11 #PHP
PHP的RSA加密解密方法以及开发接口使用
Feb 11 #PHP
php+ajax实现无刷新文件上传功能(ajaxuploadfile)
Feb 11 #PHP
PHP实现的多维数组排序算法分析
Feb 10 #PHP
ThinkPHP整合datatables实现服务端分页的示例代码
Feb 10 #PHP
PHP实现APP微信支付的实例讲解
Feb 10 #PHP
You might like
PHP统计nginx访问日志中的搜索引擎抓取404链接页面路径
2014/06/30 PHP
两种设置php载入页面时编码的方法
2014/07/29 PHP
ThinkPHP3.2.1图片验证码实现方法
2016/08/19 PHP
php文件包含目录配置open_basedir的使用与性能详解
2017/04/03 PHP
PHP的PDO大对象(LOBs)
2019/01/27 PHP
js判断手机和pc端选择不同执行事件的方法
2015/01/30 Javascript
jquery手风琴特效插件
2015/02/04 Javascript
JavaScript使用二分查找算法在数组中查找数据的方法
2015/04/07 Javascript
jQuery $.each遍历对象、数组用法实例
2015/04/16 Javascript
基于JS实现导航条之调用网页助手小精灵的方法
2016/06/17 Javascript
JS实现的自定义显示加载等待图片插件(loading.gif)
2016/06/17 Javascript
jQuery操作iframe中js函数的方法小结
2016/07/06 Javascript
浅谈JavaScript 函数参数传递到底是值传递还是引用传递
2016/08/23 Javascript
JS框架之vue.js(深入三:组件1)
2016/09/29 Javascript
详解微信小程序 wx.uploadFile 的编码坑
2017/01/23 Javascript
详解JS构造函数中this和return
2017/09/16 Javascript
Bootstrap Paginator+PageHelper实现分页效果
2018/12/29 Javascript
element ui分页多选,翻页记忆的实例
2019/09/03 Javascript
js数组的基本使用总结
2021/01/18 Javascript
[07:06]2018DOTA2国际邀请赛寻真——卫冕冠军Team Liquid
2018/08/10 DOTA
[01:01:43]EG vs VP 2018国际邀请赛淘汰赛BO3 第二场 8.24
2018/08/25 DOTA
wxpython 最小化到托盘与欢迎图片的实现方法
2014/06/09 Python
介绍Python中几个常用的类方法
2015/04/08 Python
python笔记:mysql、redis操作方法
2017/06/28 Python
Windows环境下Python3.6.8 importError: DLLload failed:找不到指定的模块
2020/11/01 Python
python-图片流传输的思路及示例(url转换二维码)
2020/12/21 Python
css3实现可拖动的魔方3d效果
2019/05/07 HTML / CSS
美国美妆网站:B-Glowing
2016/10/12 全球购物
Clarks英国官方网站:全球领军鞋履品牌
2016/11/26 全球购物
美国女性运动零售品牌:Lady Foot Locker
2017/05/12 全球购物
酒店节能减排方案
2014/05/26 职场文书
一般党员对照检查材料
2014/09/24 职场文书
药品销售员2015年终工作总结
2015/10/22 职场文书
详解php中流行的rpc框架
2021/05/29 PHP
java固定大小队列的几种实现方式详解
2021/07/15 Java/Android
windows server2008 开启端口的实现方法
2022/06/25 Servers