PHP 多进程加 PDO 丢失连接
PHP 的 pcntl
扩展实现了 PHP cli 模式下的多进程,但如果父进程创建或使用的对象,如果被子进程拿到可能会造成一些误导和错误
PDO
在使用 PDO 对 Mysql 进行操作的时候,如果在父进程实例化的 pdo 对象,在子类中继续使用,那么可能会造成 PHP warning 的警告,导致无法使用 pdo 对象对 mysql 进行操作,下面的代码可以复现这个场景:
环境
- Mysql 5.7.27
- PHP 7.3.9
<?php
$fork = 3;
$pdo = new PDO('mysql:host=127.0.0.1:3306;dbname=test', 'root', '******');
$childList = [];
for ($i = 0; $i < $fork; $i++) {
$pid = pcntl_fork();
if ($pid < 0) {
exit('fork error.');
} elseif ($pid == 0) {
// 子进程
$childId = getmypid();
echo '子进程: ' . $childId . "\n";
$sql = "select * from `t` where id = 1";
$sth = $pdo->prepare($sql);
$sth->execute();
$res = $sth->fetchAll();
echo $childId . ' 子进程查询结果: ' . "\n";
var_dump($res);
echo '子进程: ' . $childId . "结束 \n";
exit();
} else {
// 父进程
array_push($childList, $pid);
}
}
while (count($childList) > 0) {
foreach ($childList as $key => $pid) {
$res = pcntl_waitpid($pid, $status, WNOHANG);
if ($res == -1 || $res > 0) {
unset($childList[$key]);
}
}
sleep(1);
}
异常结果:
PHP Warning: PDOStatement::execute(): MySQL server has gone away
Mysql 官方描述
Some other common reasons for the MySQL server has gone away error are:
- You tried to run a query after closing the connection to the server. This indicates a logic error in the application that should be corrected.
这一条大概能对应到此处的场景中。大致意思是程序正在尝试使用已经被关闭的链接进行查询,也就是说,我们的第一个子进程在查询完成后,进程退出,这时候使用的 Mysql 链接也被释放,此时这个链接被标记为已关闭,当第二个子进程再次使用这个链接去尝试查询的时候,Mysql 就会给出错误提示
结论
- 不要在此种情况下复用父进程的连接资源
- 进程在退出的时候,可能是会把申请的连接资源标记为不可用的(和真正释放可能有差别,不释放的原因肯能是复用网络资源)