使用 Apache2(或 Nginx)处理 Http 请求。是否为每个或一组 N 个请求创建一个新进程?

Handling Http Request with Apache2 (or Nginx). Does a new process gets created for each or a set of N requests?

Web 服务器 (WS)(如 apache2 或 nginix(或容器,如 tomcat(TC))是否会创建一个新进程来处理传入请求。我担心的是支持大量并行的服务器用户(比如 20K+ 并行用户)。
我认为负载平衡发生在 Web 服务器的另一端(如果它用于前端 Tomcat 等)。因此理论上,单个 Web 服务器应该接受所有 (20K+) 传入请求,然后才能将负载分配给支持它的其他服务器。
所以,问题是:Web 服务器 (WS) 是在单个进程中处理所有这些请求,还是它巧妙地生成其他进程来帮助共享工作(我知道 "client - server" 绑定虽然发生了 - client_host:random_port 加上 server_host:fixed_port)。

参考: 在阅读本文之前:Fronting Tomcat with Apache 我认为这是一个完成所有智能工作的进程。但是在这篇文章中提到了 MPM(多处理模块)

It combines the best from two worlds, having a set of child processes each having a set of separate threads. There are sites that are running 10K+ concurrent connections using this technology.

随着它的发展,它变得越来越复杂,因为线程也像上面提到的那样被生成。 (这些不是通过调用服务方法为每个单独请求提供服务的 tomcat 线程,而是 Apache WS 上的线程,用于处理请求并将它们分发到节点进行处理)。
如果有人使用 MPM。对所有这些工作原理的进一步解释将会很棒。
像 -
这样的问题 (1) 随着子进程的产生,它的确切作用是什么。子进程是否仅用于将请求调解到 tomcat 或其他任何东西。如果是这样,那么子进程得到TC的响应后,子进程是将响应转发给父进程还是直接转发给客户端(因为它可以知道client_host: random_port 来自父进程。我不确定这在理论上是否允许,尽管子进程不能接受任何新请求,因为 fixed_port 只能绑定到一个进程,已经绑定到父进程。
(2)子进程或父进程分担什么样的负载给线程。同样,它必须与 (1) 中的几乎相同。但我不确定的是,即使理论上线程是否可以直接将请求发送给客户端。

Apache 历来使用 prefork 处理模型。在此模型中,每个请求 == 单独的操作系统 (OS) 进程。它调用 "prefork" 因为 Apache 在其中派生了一些备用进程和进程请求。如果 preforked 进程的数量不够 - Apache fork new。优点:进程可以执行其他模块或进程,而不关心它们是否执行;缺点:每个请求 = 一个进程,使用太多内存,OS fork 也可能对您的请求很慢。

Apache 的其他模型 - worker MPM。与 prefork 几乎相同,但使用的不是 OS 个进程,而是 OS 个线程。线程 - 就像轻量级进程。一个 OS 进程可以 运行 个线程使用一个内存 space。 Worker MPM 使用更少的内存和快速创建的新线程。缺点:模块需要支持线程,模块崩溃会导致所有 OS 进程的所有线程崩溃(但这对您来说并不重要,因为您仅将 apache 用作反向代理)。其他缺点:CPU switching context 在线程之间切换时。

所以是的,在你的情况下 worker 比 prefork 好得多,但是......

但是我们有 Nginx :) Nginx 使用其他模型(顺便说一句,Apache 也有事件 MPM)。在这种情况下,您只有一个进程(好吧,可以有几个进程,见下文)。怎么运行的。新请求上升特殊事件,OS 进程唤醒、接收请求、准备答案、写答案并进入休眠状态。

你可以说 "wow, but this is not multitasking" 并且会是对的。但是这个模型和简单的顺序请求处理之间有一个很大的区别。如果您需要将大数据写入慢速客户端,会发生什么情况?以同步方式,您的流程需要等待确认数据接收,并且仅在处理新请求之后。 Nginx 和 Apache 事件模型使用异步模型。 Nginx 告诉 OS 发送一些数据,将此数据写入 OS 缓冲区,然后...进入休眠状态,或处理新请求。当 OS 将发送一段数据时 - 特殊事件将发送到 nginx。因此,主要区别 - Nginx 不等待 I/O(如连接、读取、写入),Nginx 告诉 OS 他想要并且 OS 向 Nginx 发送事件比此任务准备好(套接字已连接、已写入数据或准备在本地缓冲区中读取的新数据)。此外,现代 OS 可以与 HDD (read/write) 异步工作,甚至可以将文件从 HDD 直接发送到 tcp 套接字。

当然,此 Nginx 进程中的所有数学运算都将阻止此进程并停止处理新的和现有的请求。但是,当主要工作流与网络(反向代理、将请求转发到 FastCGI 或其他后端服务器)以及发送静态文件(也是异步的)一起工作时——Nginx 可以在一个 OS 进程中同时处理数千个请求!另外,因为 OS 的一个进程(和一个线程)- CPU 将在一个上下文中执行它。

我之前是怎么说的 - Nginx 可以启动几个 OS 进程,每个进程都会被 OS 分配到独立的 CPU 核心。几乎没有理由分叉更多的 Nginx OS 进程(这样做的原因只有一个:如果你需要做一些阻塞操作,但需要后端平衡的简单反向代理 - 不是这种情况)

因此,优点:更少 CPU 上下文切换、更少内存(也与 worker MPM 相比)、快速连接处理。更多优点:Nginx 创建为 HTTP 负载平衡器,并有很多选项(在商业 Nginx Plus 中甚至更多)。缺点:如果你需要在 OS 进程中进行一些复杂的数学运算,这个过程将被阻塞(但是你所有的数学运算都在 Tomcat 中,所以 Nginx 只有平衡器)。

PS: 错别字稍后修复,过时了。另外,我的英语不好,所以总是欢迎修复:)

PPS:回答有关 TC 线程数的问题,在评论中提出(对于 post 作为评论来说太长了):

了解它的最佳方式 - 使用压力加载工具对其进行测试。因为这个数字取决于应用程序配置文件。响应时间不够好,无法帮助回答。因为,例如,200 毫秒的 100% 数学(100% cpu 界限)与 50 毫秒的数学 + 150 毫秒的睡眠等待数据库答案之间的巨大差异。

如果应用程序 100% CPU 绑定 - 可能每个内核一个线程,但在实际情况下,所有应用程序也在 I/O 中花费了一些时间(接收请求,向客户端发送答复)。

如果应用程序与 I/O 一起工作并且需要等待其他服务(例如数据库)的响应,则此应用程序会在睡眠状态中花费一些时间并且 CPU 可以处理其他任务。

因此最好的解决方案是创建接近实际负载的请求数量,并且 运行 压力测试增加并发请求的数量(当然还有 TC 工作人员的数量)。找到可接受的响应时间并修复此线程数。当然,之前需要检查一下是不是数据库故障。

当然,这里我只讨论动态内容,从磁盘请求静态文件必须在tomcat之前处理(例如通过Nginx)。