问题描述
有两个PHP脚本,start.php
和processes2.php
这两个脚本都连接一台相同的 Redis
执行过程:
假设此时 redis 的连接数为 0。我是用redis-cli info | grep connected_clients
查看的。
命令行执行 path/to/php start.php
,在这个脚本中会做以下两件事:
- 连接 Redis
- 用shell_exec启动脚本
processes2.php
然后start.php
退出
在processes2.php
中只会做一件事,那就是连接Redis
问题点:
执行start.php
前用redis-cli info | grep connected_clients
查看连接数为0。
执行start.php
,连接Redis后但未启动脚本processes2.php
时查看Redis连接数1。
在start.php
启动了processes2.php
并退出,当processes2.php
连接Redis后查看连接数为2。
当processes2.php
执行完后,再查看连接为又恢复为0。
也就是说进程1启动了进程2,进程1运行完退出了,进程2在运行中但1中的连接却没释放!start.php
执行完后,用ps -ef | grep php
已经找不到了,是真的退出了。
我这里表达的连接数是指Redis服务端显示的已连接的客户端的数量,两个进程是不是使用的同一个连接连接的
我没确认,本人不才,主要是不知道怎么确认-_-||。
我试过的方式
- 用函数
exec()
、system()
也是一样的效果 -
start.php
的最后调用下exit()
、die
也是一样效果 - 我试了用Redis插件的的connetc和pconnect都是一样的结果
- 设置Redis连接空闲一定时间后就关闭
config set timeout 10
,可以让连接数慢慢下降。但这不是我想要的 - 在启动
processes2.php
之前手动调用close
关闭连接,这样也可以,但也不是我想要的。因为之后的业务可能要用到Redis那我不是还要连一次。
求解答
- 为啥会这样,背后的原理是啥?
- 有没有啥解决方案,让
start.php
退出后,它的连接啥的也就自动释放掉、
模拟代码
代码可以复制到本地后运行,本来准备上传的发现不能上传文件,就只能这样贴了
配置文件
<?php
return [
'host' => '127.0.0.1',
'port' => 6379
];
start.php
<?php
$config = include __DIR__ . '/config.php';
echo "process 1 START" . PHP_EOL;
echo "连接Redis..." . PHP_EOL;
//连接 Redis
$cacheInstance = new \Redis();
try {
$cacheInstance->connect($config['host'], $config['port'], 0);
} catch (\Exception $exception) {
echo sprintf("连接redis失败host:%s, port:%s, timeout:%s", $config['host'], $config['port'], 0) . PHP_EOL;
exit();
}
if (!empty($config['password'])) {
try {
$cacheInstance->auth($config['password']);
} catch (\Exception $exception) {
echo 'redis授权失败password:' . $config['password'] . PHP_EOL;
exit();
}
}
echo "Redis 连接成功" . PHP_EOL;
//取 Redis 当前连接数
//用php-redis插件的info()方法获取到的不太准所以直接用shell获取
$clientNum = `redis-cli info | grep connected_clients`;
$clientNum = preg_replace('/[^\d]/', '', $clientNum);
echo "\033[32m 进程1连接Redis后的连接数:\033[37m" . $clientNum . PHP_EOL;
//shell 启动第二个进程
echo "启动2个进程2..." . PHP_EOL;
$logFile2 = __DIR__ . '/process2.log';
$processFile = __DIR__ . '/processes2.php';
`nohup /app/php/bin/php {$processFile} > {$logFile2} 2>&1 &`;
echo "启动进程2结束" . PHP_EOL;
echo "进程2的日志将写入{$logFile2}" . PHP_EOL;
$desc = <<<desc
此后进程1会结束
但连接的 Reids 不会释放,可查看进程2的日志对比得出
假设没运行进程1时。redis 连接的客户端数为n,则
进程1启动后为n+1
进程2启动后为n+2
进程2结束后为n
desc;
echo $desc . PHP_EOL;
echo 'exit!' . PHP_EOL;
processes2.php
<?php
$config = include __DIR__ . '/config.php';
echo "进程2启动" . PHP_EOL;
echo "连接 Redis..." . PHP_EOL;
$cacheInstance = new \Redis();
//连接 Redis
try {
$cacheInstance->connect($config['host'], $config['port'], 0);
} catch (\Exception $exception) {
echo sprintf("连接redis失败host:%s, port:%s, timeout:%s", $config['host'], $config['port'], 0) . PHP_EOL;
exit();
}
if (!empty($config['password'])) {
try {
$cacheInstance->auth($config['password']);
} catch (\Exception $exception) {
echo 'redis授权失败password:' . $config['password'] . PHP_EOL;
exit();
}
}
echo "进程2连接 Redis 成功" . PHP_EOL;
$startTime = time();
//取 Redis 当前连接数
echo "开始获取 Redis 连接的客户端 60秒后退出" . PHP_EOL;
$writeFileName = __DIR__ . '/output_from_process2.log';
do {
//用php-redis插件的info()方法获取到的不太准所以直接用shell获取
$clientNum = `redis-cli info | grep connected_clients`;
$clientNum = preg_replace('/[^\d]/', '', $clientNum);
echo '当前客户端数:' . $clientNum . ' waite 1s...' . PHP_EOL;
sleep(1);
} while ( time() - $startTime < 60 );
$desc = <<<desc
与进程一输出的 Redis 连接数对比
可以看到进程1已经退出了,但是连接并没有释放,仍然占用着的连接数
进程2结束后,可用命令 redis-cli info | grep connected_clients 查看 Redis 上连接的客户端数
应该可以看到被这两个进程占用连接数已经释放
desc;
echo $desc . PHP_EOL;
echo 'exit!' . PHP_EOL;
PHP的redis扩展底层是有连接池机制的,你打开的连接,在底层不会立即关闭,而是放到连接池中,等待下一次使用。