ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

prefork 与 PPC 模式区别、Java 中间件是否使用、Apache 256 并发配置详解

prefork 与 PPC 模式区别、Java 中间件是否使用、Apache 256 并发配置详解 prefork 与 PPC 模式区别、Java 中间件是否使用、Apache 256 并发配置详解这篇笔记专门拆三个问题prefork 和 PPC 到底啥区别为什么说它俩都不行现在 Java 用的中间件里有哪些是 prefork 模式剧透几乎没有这里有个常见误解默认最大 256 个并发连接指的是哪个中间件、哪个配置能调大吗关联文档[[fork之后为什么需要IPC-从文件描述符继承说起]]、[[进程间通信方式详解-IPC]]——prefork 的痛点本质上就是 fork 之后父子进程通信的问题。一、先把两个概念摆正PPC vs prefork这俩都是多进程并发模型思路一脉相承区别只在什么时候 fork。1. PPC Process Per Connection每连接一进程每来一个连接主进程现场fork一个子进程专门伺候这个连接连接断开子进程就退出。客户端A 连进来 ── 主进程 fork ── 子进程A 处理 ── 处理完销毁 客户端B 连进来 ── 主进程 fork ── 子进程B 处理 ── 处理完销毁 每个连接都重复一次 fork 销毁的完整流程致命缺点fork 太贵Linux 下fork()虽然有写时复制COW优化但每次连接都要走一遍复制进程地址空间描述、分配 PID、建页表高并发下扛不住。父子进程通信复杂子进程处理结果要回传父进程得靠 IPC管道、共享内存、信号量……见 [[进程间通信方式详解-IPC]]。并发数有限每个进程占独立内存进程数上不去。2. prefork 预先 fork进程池主进程启动时一次性预先 fork 出一池子进程N 个 worker它们就位后阻塞等待。来连接了从池子里挑一个空闲进程去处理处理完不销毁回池子继续等下一个。启动时主进程 ──fork×N── [worker1, worker2, ... workerN] 全部就位、阻塞等待 客户端A 连进来 ── worker1 接走处理 ── 处理完回池子继续等 客户端B 连进来 ── worker2 接走处理 ── 处理完回池子继续等 不再每次 fork复用进程比 PPC 好在哪省掉了每连接一次 fork的开销进程复用。为啥还是不行和 PPC 一样的痼疾仍然是多进程模型每个 worker 独立内存空间内存占用大一个进程几十 MB 是常态几百个就是几 GB。父子/兄弟进程之间通信仍然要靠 IPC复杂度没降。并发数 进程数受机器内存和 PID 上限卡死扛不住几万连接的现代场景。一句话prefork 是 PPC 的进程池优化版但本质还是多进程所以原文说它和 PPC 一样存在父子进程通信复杂、支持的并发连接数量有限的问题。3. 对比速查表维度PPC每连接一进程prefork预先 forkfork 时机每个连接来时现场 fork启动时一次性 fork 一池子进程是否复用否用完即销毁是处理完回池子等下一个fork 开销每连接一次高摊到启动期连接期几乎无内存占用高峰值与并发数成正比高固定 N 个进程常驻进程间通信需要 IPC仍需要 IPC并发上限受进程数限制受进程数池大小限制典型代表早期 Web 服务器Apache MPM prefork二、现在 Java 的中间件有哪些是 prefork 模式直接结论几乎没有 Java 中间件采用 prefork多进程模型。这一点很容易被原文带偏——原文讲 Apache prefork而 Apache HTTP Server 是C 语言写的不是 Java 中间件。很多人把Apache和Java 中间件混为一谈但它们是两回事。下面拆开说。1. Java 中间件几乎都用线程模型不是多进程模型JVM 启动代价极高而且fork()在 JVM 里有天然的坑fork 出的子进程会复制父进程整个堆和线程状态但其他线程在 fork 瞬间的状态是未定义的极易死锁/损坏。所以 Java 生态压根不走多进程并发而是中间件并发模型说明Tomcat线程池thread-per-request NIO一个请求一个线程线程从池里取NIO Connector 用 Selector 多路复用降线程数。不是 preforkJetty线程池 NIO/异步同上线程模型UndertowXNIO 线程池基于 NIO 的线程模型NettyReactor 主从线程组 NIO事件驱动线程模型单/少量线程扛大量连接这些都是线程级并发进程只有一个 JVM。和 prefork 的多进程 IPC是两条技术路线。2. 那 Tomcat 的 APR/native 模式算 prefork 吗不算。Tomcat 的 APR/native Connector 底层确实调用了 Apache Portable Runtime和 Apache httpd 同源的 C 库但它用的是APR 的线程 poll 机制跑的还是 JVM 进程内的线程不是 fork 出一堆进程。别被APR这个名字和 Apache 的关系误导。3. 真正prefork-like的典型代表是谁不是 Java是PHP-FPMPHP-FPM 启动时预 fork 一池子 worker 进程pm static/dynamic 每个 worker 是独立进程处理完一个请求回池子等下一个这和 prefork 的思路几乎一模一样。所以 prefork 这种模型的现代继承者主要在 PHP-FPM而不是 Java 中间件。原因是 PHP 解释器本身没有线程安全的可靠实现、且请求间要彻底隔离只能靠多进程而 Java 天生多线程没必要走这条路。4. 那 nginx 呢nginx 启动时也会fork出多个 worker 进程——但这不是 prefork。nginx 的每个 worker 是事件驱动epoll多路复用一个 worker 进程能扛成千上万的连接是少量进程 每进程大量连接和 prefork 的一个连接一个进程完全相反。别看到预先 fork 多个进程就归到 prefork。三、“默认最大 256 个并发连接”——哪个中间件、哪个配置、能调大吗1. 指的是谁指的是Apache HTTP Serverhttpd的 MPM prefork 模式。原文那句默认情况下最大支持 256 个并发连接说的就是它。⚠️ 再强调一次Apache httpd 是C 写的 Web 服务器常作为 Java 应用Tomcat/Spring Boot前面的反向代理或静态资源服务器但它本身不是 Java 中间件。2. 哪个配置项配置项名字随 Apache 版本变过Apache 版本配置项默认值作用2.2 及更早MaxClients256同时能存在的 prefork worker 进程上限 并发连接上限2.4 起MaxRequestWorkers256同上只是改了名语义更准处理请求的工作者上限也就是说256这个默认数2.2 叫MaxClients2.4 叫MaxRequestWorkers本质都是prefork 模式下最多多少个 worker 进程同时干活。3. 配置文件在哪随发行版不同位置不一样常见三处# Debian / UbuntuApache 2.4/etc/apache2/mods-available/mpm_prefork.conf# RHEL / CentOS / Rocky/etc/httpd/conf.modules.d/00-mpm.conf# 加载 mpm_prefork 模块/etc/httpd/conf.d/...# 或在主配置里覆写# 源码编译安装 / 通用主配置/usr/local/apache2/conf/httpd.conf /usr/local/apache2/conf/extra/httpd-mpm.conf一个典型的mpm_prefork.conf长这样IfModule mpm_prefork_module StartServers 5 # 启动时预 fork 的进程数 MinSpareServers 5 # 最少空闲进程数 MaxSpareServers 10 # 最多空闲进程数 MaxRequestWorkers 256 # ← 就是这个并发上限2.4 MaxConnectionsPerChild 0 # 每个进程处理多少请求后回收0不回收 /IfModule # Apache 2.2 里把 MaxRequestWorkers 换成 MaxClients并多一个 ServerLimit4. 能调大吗能但有坑能调大直接改MaxRequestWorkers2.2 的MaxClients即可但有三道坎必须一起考虑坎 1ServerLimit必须跟着调Apache 2.4 的硬上限MaxRequestWorkers不能超过ServerLimit。ServerLimit默认256所以你光改MaxRequestWorkers1000是没用的——必须先把ServerLimit也抬上去IfModule mpm_prefork_module ServerLimit 1000 # 必须先抬这个 MaxRequestWorkers 1000 # 才能生效 StartServers 10 MinSpareServers 10 MaxSpareServers 50 MaxConnectionsPerChild 1000 /IfModuleApache 2.4 的ServerLimit理论上限是2000002.2 同理但实际没人设这么大见坎 2。坎 2内存是真正的天花板最容易翻车prefork 每个 worker 是独立进程每个进程都要把 Apache 加载的模块、PHP 等都占一份内存。粗略估算单进程内存 ≈ 10 MB纯静态~ 50 MB加载 PHP/大量模块 设每进程 30 MB 256 进程 → ~7.5 GB 1000 进程 → ~30 GB 5000 进程 → ~150 GB ← 内存直接爆所以调多大不是拍脑袋要先算可用内存 / 单进程内存 你能设的上限否则进程数一上去系统开始换页swap甚至 OOM Killer性能断崖式下跌比不调还惨。坎 3prefork 本就不该扛高并发即便内存够prefork 一个连接一个进程的设计到几千并发就吃力。真有高并发需求正确做法是换模型而不是把 prefork 调到天上去同样是 Apache切到MPM event或MPM workerworker/event 是多进程 每进程多线程event 还支持 keepalive 连接异步化单台轻松上万并发。或者直接上nginx事件驱动worker 进程数 CPU 核数一个 worker 扛几万连接。结论256 能调大但 prefork 这个模型的天花板就在几千量级调到几千还行再往上请换模型MPM event / worker / nginx别硬刚。5. 怎么验证改没改生效# 看当前加载的是哪个 MPMapache2ctl-V# Debian/Ubuntu看 Server MPM 字段httpd-V# RHEL/CentOS# 看当前实际生效的 prefork 配置apache2ctl-M|grepmpm# 确认加载了 mpm_preforkapache2ctl-t-DDUMP_MODULES# 列模块# 改完先测语法、再 graceful 重载apache2ctl configtest systemctl reload apache2# 或 apachectl graceful四、一图收尾几个概念别再混并发模型谱系简化 ├─ 多进程 │ ├─ PPC 每连接一进程现场 fork ← 老 │ ├─ prefork 预 fork 进程池 ← Apache MPM prefork / PHP-FPM │ └─ nginx 多进程 每进程 epoll 多路复用 ← 不是 prefork └─ 多线程 / 事件驱动 ├─ Tomcat/Jetty/Undertow 线程池 NIO ← Java 中间件基本在这条线 ├─ Netty Reactor NIO └─ Apache MPM event/worker 多进程多线程 ← Apache 的高并发版五、一句话回答最初的三个问题区别prefork 是 PPC 的进程池优化版——把每连接现场 fork改成启动时预 fork 一池子复用省掉每次 fork 的开销但本质都是多进程内存占用大、靠 IPC 通信、并发受进程数限制的痼疾没变。Java 中间件几乎没有用 prefork 的。Tomcat/Jetty/Undertow/Netty 全是线程 NIO模型APR Connector 也是线程不是进程。prefork 的现代典型代表是PHP-FPM不在 Java 侧。256 并发指Apache HTTP Server 的 prefork MPM配置项是 2.4 的MaxRequestWorkers2.2 的MaxClients默认 256。能调大但必须同时抬ServerLimit并且要按可用内存 / 单进程内存算上限几千以内可玩再高就该换 MPM event/worker 或 nginx。
返回列表