为何使用Cluster模块后请求仍在同一PID/线程运行?未实现多线程?
为什么Express+Cluster没有以多线程模式处理请求?
问题核心原因
- 浏览器TCP连接复用:现代浏览器会对同一域名的请求复用已建立的TCP连接,而Cluster模块默认的轮询负载均衡是基于新连接分配Worker进程的。如果在同一个浏览器窗口连续发起请求,所有请求会复用同一个连接,因此都会被同一个Worker进程处理,导致你看到的PID一致。
- 代码逻辑冗余(非直接原因):
else if (cluster)这个判断完全多余,因为cluster是已加载的模块对象,永远为真。正确的写法应该是直接用else,明确子进程的启动分支。
验证与修复方法
正确验证多进程:
- 使用不同浏览器、Postman多窗口同时发起请求,或者在命令行用
curl多次执行(每次请求都是新TCP连接),就能看到请求被分配到不同PID的Worker进程。 - 比如打开多个命令行窗口,同时执行:
curl http://localhost:8000/ curl http://localhost:8000/timer
- 使用不同浏览器、Postman多窗口同时发起请求,或者在命令行用
修正代码逻辑:
将子进程的判断分支改为else,让代码逻辑更清晰:const express=require("express"); const app=express(); const cluster=require('cluster'); const os=require('os'); function delay(duration){ const start=Date.now(); while(Date.now()-start<duration){} } app.get('/',(req,res)=>{ res.status(200).send(`request running at id ${process.pid}`) }) app.get('/timer',(req,res)=>{ delay(5000) res.status(200).send(`Timer running at id ${process.pid}`) }) if(cluster.isPrimary){ console.log(`Master running...`) const NUM_WORKERS=os.cpus().length for(let i=0;i<NUM_WORKERS;i++){ cluster.fork() } } else { console.log(`Fork running at ${process.pid}`) app.listen(8000,()=>{ console.log(`Server is running at port ${8000}`) }) }关于同步阻塞的补充:
你的delay函数是同步阻塞操作,一旦某个Worker处理了/timer请求,该Worker会被阻塞5秒无法处理其他请求,但其他Worker仍能正常响应请求。用多客户端同时请求时,就能明显看到不同PID的Worker在处理请求。
内容的提问来源于stack exchange,提问作者Johnny Nicolas
相关产品推荐
相关产品推荐

