5

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

在 PHP 中,当您使用 pcntl_fork 时,您使用 mysql_connect 创建的所有连接都会被复制,这有点问题 - php docs and SO 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 没有意义。
4

3 回答 3

2

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

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

如果您确实需要连接,您应该考虑使用另一种语言来完成这项工作。PHPs cli 本身并不是一个“典型”用例(它是在 4.3 中添加的),多处理与其说是受支持的功能,不如说是一种 hack。

于 2011-04-20T16:15:31.993 回答
0

如果孩子调用 exec() 或 _exit() 相当快,你没事。问题是孩子是否坚持并持有文件描述符的副本。

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

于 2011-04-20T16:30:29.930 回答
0

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

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

SIGCHLD是当其子进程之一完成时由父进程接收的信号。

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

nanosleep()因为信号返回时(即在足够的时间过去之前),MySQL 库会感到困惑并报告错误“MySQL 服务器已消失”,并且无法再使用连接。这是一个误报,MySQL 服务器仍然在那里等待查询,但客户端代码被错误的时间到达的信号所欺骗。

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

另请阅读这个答案和我在类似问题上写的这个答案(这是相同的信息,但有更多细节和解释。)

于 2017-08-19T17:21:54.317 回答