如何在pcntl_fork之后将我的mysql连接保留在父进程中?

时间:2011-04-20 15:59:20

标签: php mysql linux fork pcntl

众所周知,当你分叉时,孩子会获得所有内容的副本,包括文件和网络描述符 - man fork

在PHP中,当您使用pcntl_fork时,会复制使用mysql_connect创建的所有连接,这有点问题 - php docsSO question。在这种情况下常识说关闭父连接,创建新的并让孩子使用旧连接。但如果说父母需要创造许多孩子几秒钟呢?在这种情况下,您最终会创建加载的新连接 - 每一个叉子都有一个。

这在代码中意味着什么:

while (42) {

  $db = mysql_connect($host, $user, $pass);

  // do some stuff with $db
  // ...

  foreach ($jobs as $job) {
        if (($pid = pcntl_fork()) == -1) {
            continue;
        } else if ($pid) {
            continue;
        }
    fork_for_job($job);
  }

  mysql_close($db);
  wait_children();
  sleep(5);
}

function fork_for_job($job) {

  // do something. 
  // does not use the global $db 
  // ...

  exit(0);
}

好吧,我不想这样做 - 那就是与数据库的连接太多了。理想情况下,我希望能够实现与此类似的行为:

$db = mysql_connect($host, $user, $pass);

while (42) {

  // do some stuff with $db
  // ...

  foreach ($jobs as $job) {
        if (($pid = pcntl_fork()) == -1) {
            continue;
        } else if ($pid) {
            continue;
        }
    fork_for_job($job);
  }

  wait_children();
  sleep(5);
}

function fork_for_job($job) {

  // do something
  // does not use the global $db 
  // ...

  exit(0);
}

你认为有可能吗?

其他一些事情:

  • 这是php-cli脚本
  • 我在第一个例子中尝试过使用mysql_pconnect,但据我所知,没有区别 - mysql服务器收到了很多新连接。也许那是因为它是cli和pconnect不能像在mod_php中那样工作。正如Marc注意到的那样 - 在php-cli中的pconnect没有意义。

3 个答案:

答案 0 :(得分:2)

你唯一能做的就是让你的孩子等到彼此的孩子完成工作。这样您就可以使用相同的数据库连接(前提是没有任何同步问题)。但是当然你会有很多进程,这也不是很好(根据我的经验,PHP有很大的内存使用率)。如果有多个进程访问同一个数据库连接不是问题,您可以尝试创建共享连接的进程的“组”。所以你不必等到每个工作完成(你可以在整个团队完成后清理)并且你没有很多连接......

您应该问问自己是否真的需要为您的工作进程建立数据库连接。为什么不让父进程获取数据并将结果写入文件?

如果确实需要连接,则应考虑使用其他语言进行工作。 PHPs cli本身不是一个“典型的”用例(它是在4.3中添加的),而且多处理更像是一个黑客而不是支持的功能。

答案 1 :(得分:0)

如果孩子很快调用exec()或_exit(),你就没问题了。问题是如果孩子坚持并保留文件描述符的副本。

如果PHP有一个API,你也可以使用posix_spawn。这可能效果很好。

答案 2 :(得分:0)

我的建议(根据同一问题的个人经验)是在pcntl_fork()之前关闭连接,然后根据需要在父进程和/或子进程中打开新连接。

如果您在父进程中打开新连接 ,则必须阻止SIGCHLD信号(使用pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD))。儿童过程中不需要特别小心(除非他们也开始自己的孩子,以这种方式成为父母。)

SIGCHLD是父进程在其中一个子进程完成时收到的信号。

在与服务器通信期间,MySQL客户端库使用nanosleep()暂停程序的执行一段时间。 sleep()函数会在时间过去时返回,但如果进程在挂起时收到信号,它们也会在时间之前返回

nanosleep()由于信号而返回时(即在经过足够的时间之前),MySQL库会混淆并报告错误" MySQL服务器已经消失了#34;并且连接不能再使用了。这是一个误报,MySQL服务器仍然在那里等待查询,但客户端代码被错误时刻到达的信号所欺骗。

如果您有兴趣接收SIGCHLD信号,那么您可以在运行MySQL查询之前阻止它,然后再次取消阻止它(以避免在与MySQL服务器通信期间收到它。

同时阅读this answerthis answer我写了类似的问题(它是相同的信息,但有更多细节和解释。)