运行 个 PHP-FPM 子节点比 CPU 个核心数有意义吗?

Does it make sense to run more PHP-FPM children than number of CPU cores?

假设我有一个具有 4 个核心和 4 个线程的 CPU,运行 是否有意义,例如8 PHP-FPM 工人通过设置 pm.max_children = 8 选项?就我而言,具有 4 个线程的 CPU 最多只能 运行 4 个“真正”并行进程。如果由于这 8 个进程之间的上下文切换而导致 CPU 时间丢失,是否会导致开销?

相比之下,Node.js 集群模式文档建议 运行 最多 workers/children 核心数。同样的建议在这里不适用吗?

一般的答案是肯定的,因为虽然你不能 运行 在 中并行 那么多线程,但你可以 运行 它们 同时.

要理解的关键是,在大多数实际应用程序中,处理请求所花费的很多时间都没有花在使用本地 CPU - 它花在了等待数据库查询、外部 API,甚至磁盘上使用权。如果每个 CPU 核心有一个线程,那么 CPU 一直处于空闲状态。允许额外的线程,一个可以使用 CPU 而另一个正在等待外部数据。

只有当您的应用程序非常不寻常并且花费 100% 的时间使用 CPU 时,限制每个内核一个线程才有意义。

这不适用于 node.js 的原因是它使用 异步 代码在单个线程 中实现并发 :您可以告诉当前线程“开始执行此操作,并在等待结果的同时,继续处理不同的请求”。这对于本机 PHP 是不可能的,它使用“无共享”方法——每个请求都有自己的线程或进程——但有一些项目,如 Swoole 和 Amp,增加了对这种异步方法的支持。

接受的答案很好,但没有正确解决问题。

PHP 不在单核上使用线程和 运行s。 PHP-FPM 生成许多工作进程,因此您可以 运行 在多核上处理您的进程。

了解 OS 如何使用进程上下文切换来同时处理多个进程很重要。如果你只有一个核心,你仍然可以 运行 同时在你的机器上运行多个进程,原因是进程上下文切换。这意味着 OS 将使用单核并在进程之间动态切换,根据各种因素一次处理每个进程,例如如果一个进程正在等待 I/O,需要多长时间进程已经运行ning,如果另一个进程作为更高优先级等。重要的部分是进程上下文切换需要一些时间,并且单核在多个进程之间共享。

如果您有多个内核,进程可以在每个内核上并行执行,但是您的 运行ning 进程很可能仍然多于内核,因此进程上下文切换仍然会发生,只是速度较低.

建议将 pm.max_children 设置为高于 CPU 核心的值的原因是,在大多数情况下,您的 php 进程不会进行密集 CPU 任务,但主要是等待 I/O,例如等待 SQL 结果,等待某些 curl 响应或某些磁盘读写响应。这些操作称为 I/O 阻塞,并且是请求中消耗大部分时间的操作。通过将 pm.max_children 设置为比内核更高的值(有时甚至是内核数量的 5-10 倍),您可以从上下文切换中受益 OS 将在进程处于 blocking/idle状态。

很可能有超过 20 个 PHP 进程 运行ning 只是在等待 IO。如果您将 pm.max_children 设置为核心数,比如说 8,核心可能不会做太多事情,很多请求会堆积起来,响应速度会很慢。

如果您确定您的 php 进程没有阻塞 I/O 并且仅执行一些计算,您实际上可能会从仅设置完全相同的数量中获益更多 pm.max_children作为您的核心,原因是进程上下文切换会减慢速度,并且拥有更多 运行ning 进程会占用更多资源。然而,这种情况并不常见,很可能您的进程确实有 I/O 阻塞和空闲时间。

有一篇很好的文章深入探讨了 Linux here 上的进程上下文切换。

swoole PHP 扩展中还使用了一种叫做协程的东西。协程也使用上下文切换来执行并发,但是这是通过编程方式完成的,它消耗的资源少得多,并且比 OS 上下文切换快得多。如果使用 swoole 则不需要 php-fpm 因为它更快,但它还有其他您需要关心的问题。然而,对于 swoole,建议您将 worker 设置为与核心一样多,以避免 OS 上下文切换。您可以拥有数千个协程而不会对性能产生太大影响。

Nodejs使用事件循环,类似于swoole的协程。建议设置 worker 以匹配您的核心的原因是避免 OS 上下文切换并使用内置的上下文切换,因为它更快更轻。