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 就會給出錯誤提示
結論
- 不要在此種情況下複用父進程的連接資源
- 進程在退出的時候,可能是會把申請的連接資源標記爲不可用的(和真正釋放可能有差別,不釋放的原因肯能是複用網絡資源)