pconnect连接函数是phpredis中用于client连接server的api,详见:phpredis api

pconnect, popen


Description: Connects to a Redis instance or reuse a connection already established with pconnect/popen.

The connection will not be closed on close or end of request until the php process ends. So be patient on too many open FD's (specially on redis server side) when using persistent connections on many servers connecting to one redis server.

Also more than one persistent connection can be made identified by either host + port + timeout or host + persistent_id or unix socket + timeout.

This feature is not available in threaded versions. pconnect and popen then working like their non persistent equivalents.

Parameters

host                  : string. can be a host, or the path to a unix domain socket
port                  : int, optional, default is 6379
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)
read_timeout    : float, value in seconds (optional, default is 0 meaning unlimited)

Return value

BOOLTRUE on success, FALSE on error.

Example
$redis->pconnect('127.0.0.1', 6379);
$redis->pconnect('127.0.0.1');                  // port 6379 by default - same connection like before.
$redis->pconnect('127.0.0.1', 6379, 2.5);       // 2.5 sec timeout and would be another connection than the two before.
$redis->pconnect('127.0.0.1', 6379, 2.5, 'x');  // x is sent as persistent_id and would be another connection than the three before.
$redis->pconnect('/tmp/redis.sock');            // unix domain socket - would be another connection than the four before.

 

 

pconnect 问题:

1.  php process ends是指一次php执行完结,还是fpm的终结?如果是后者,那意味着即使一次php执行完毕,redis连接也不会被释放,下一次执行时redis连接会被重用。

2. The connection will not be closed on close是 说如果使用了pconnect, 即使在代码中显示的调用close(), 也不会关闭连接?

带着这两个问题,我们做下实验,深入看一下pconnect究竟做了些什么。

 

准备工作

环境: nginx + fpm  +  php 7.1.9

我们将 php-fpm.conf 配置为

pm.max_children = 1
pm.start_servers = 1
pm.max_spare_servers = 1

这样,我们的页面请求会由一个确定的fpm进程执行,方便strace跟踪。

对应页面请求的php代码:

$ip = "10.136.30.144"; 
$port = 7777; 
$redis = new Redis();

$redis->pconnect($ip, $port, 1);
$key = "test";
$value = "this is test";

$redis->set($key, $value);
$d = $redis->get($key);
var_dump($d);

代码的功能很简单,连接redis,先设置一个值,再取出。

 

测试问题1

思路:使用strace观察fpm的系统调用,如果连接的生命周期是一次php执行,那么每次页面调用,都会有connect系统调用,用以连接redis;如果连接的生命周期是fpm的终结,那么只有第一次页面调用会有connect系统调用 ,之后由于连接被重用,无需connect,直接发命令请求即可。

启动一个新的fpm(进程号28082)。

执行: strace -p 28082 -s 1024 -o redis_1

记录一次页面请求的系统调用。如下图所示:

这里写图片描述

可以看到进程先建立了socket连接(文件描述符为9)。

然后给reids发一系列命令,最终取到“this is test”的结果串。

且没有关闭连接相关的redis命令或系统调用。

页面请求结束后,我们执行

lsof -n -p 28082

这里写图片描述

可以看到,fpm进程仍然保有一个到10.136.30.144的reids连接,其文件描述符为9(这与strace的结果一致)。

执行

strace -p 28082 -s 1024 -o redis_2

 

记录 第二次页面请求的系统调用,得到下面结果。

这里写图片描述
与第一次请求的区别是:省去了建立连接的过程,直接发送reids命令,得到结果!

再使用lsof -n -p 28082查看fpm打开的文件描述符,结果与上文件相同。

说明,连接的确是被重用的,没有新建。

执行第6次页面请求(因为我们在准备工作中的配置,此时fpm已经是一个新的进程了),用lsof查看进程打开的文件描述符。

这里写图片描述
我们发现,虽然仍然有描述符为9的reids连接,但两个tcp连接的临时端口不同了,也就是连接改变了!

至此,我们得出问题1的结论:

当使用pconnect时,连接会被重用,连接的生命周期是fpm进程的生命周期,而非一次php的执行

 

测试问题2

为了对比,我们先看一下,使用connect连接redis,并调用redis->close()的系统调用。(将上述代码中的pconnect改为connect, 同时在最后加入redis->close())

这里写图片描述
我们看到,除了建立连接外,在程序结尾,还向reids发送了quit命令,并关闭了连接的文件描述符。

接下来,我们看在使用pconnect后,redis->close()有何表现

代码调整为:

$ip = "10.136.30.144";
$port = 7777;
$redis = new Redis();
$redis->pconnect($ip, $port, 1);
$key = "test";
$value = "this is test";
$redis->set($key, $value);
$d = $redis->get($key);
var_dump($d);
$redis->close();

try{
    $redis->get($key);
} 
catch (Exception $e){
    echo $e->getMessage();
}

我们直接看第2次执行页面请求的系统调用

这里写图片描述

并没有建立连接,同样是直接发送命令得到结果。说明连接被重用。同时,没有向reids server发送quit命令,也无关闭连接的系统调用。

但注意,页面请求的返回结果:

这里写图片描述

至此,我们得出问题2的结论:

如果代码中使用pconnect, close的作用仅是使当前php不能再进行redis请求,但无法真正关闭redis长连接,连接在后续请求中仍然会被重用,直至fpm进程生命周期结束。

 

结论

1. 当使用pconnect时,连接会被重用,连接的生命周期是fpm进程的生命周期,而非一次php的执行。

2. 如果代码中使用pconnect, close的作用仅是使当前php不能再进行redis请求,但无法真正关闭redis长连接,连接在后续请求中仍然会被重用,直至fpm进程生命周期结束。

 

 

原文深入php redis pconnect