Express+PM2集群+Nginx负载均衡:请求无法并行运行问题求助
解决Express多实例+Nginx反向代理下请求串行阻塞的问题
从你的日志和配置来看,核心问题并不是Express实例本身不能并行处理请求,而是请求被串行转发到各个实例了——你能看到每个实例都是等前一个请求处理完才接收新请求,这说明请求没有同时到达多个实例,而是排队逐个分发。下面是具体的排查和解决步骤:
一、问题根源:HTTP/1.1的队头阻塞+连接复用
你的测试代码用axios默认配置发起请求时,会复用同一个TCP连接(HTTP/1.1默认开启keepalive)。而Nginx在处理单个TCP连接上的多个请求时,会按顺序转发到上游实例,不会把同一个连接里的请求分配到不同实例,这就导致了请求串行处理的现象。
二、解决方案:从客户端和Nginx两端优化
1. 客户端:强制发起多个TCP连接
修改axios的配置,禁用连接复用或者增大允许的并发连接数,让每个请求可以通过不同的连接发送:
const axios = require('axios'); const http = require('http'); const https = require('https'); // 创建自定义axios实例,配置多连接支持 const axiosInstance = axios.create({ httpAgent: new http.Agent({ keepAlive: false, // 禁用连接复用,每个请求新建连接 maxSockets: 10 // 允许同时建立10个连接 }), httpsAgent: new https.Agent({ keepAlive: false, maxSockets: 10 }) }); async sendRequest() { const startTime = performance.now(); console.log("Sending request"); const els = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; const promises = els.map(() => axiosInstance.get("http://my_server_with_nginx:8000")); let results = await Promise.all(promises); console.log(results); const stopTime = performance.now(); console.log("Time of request: " + (stopTime - startTime)); }
2. Nginx:优化上游连接配置
调整Nginx的上游和客户端连接设置,确保它能并行分发请求到多个Express实例:
upstream test_upstream { least_conn; # 按最少连接数分发,比默认的轮询更合理 server 127.0.0.1:3000; server 127.0.0.1:3001; server 127.0.0.1:3002; server 127.0.0.1:3003; keepalive 32; # 与上游保持32个空闲连接,避免频繁新建连接 } server { listen 8000; keepalive_timeout 65; # 客户端连接超时时间 keepalive_requests 100; # 单个客户端连接允许的最大请求数,防止长期占用 location / { # 启用与上游的HTTP/1.1 keepalive proxy_http_version 1.1; proxy_set_header Connection ""; # 你的CORS配置保持不变 proxy_hide_header Access-Control-Allow-Origin; add_header Access-Control-Allow-Origin * always; proxy_hide_header Access-Control-Allow-Methods; add_header Access-Control-Allow-Methods "GET,POST,DELETE,PUT,OPTIONS" always; proxy_hide_header Access-Control-Allow-Headers; add_header Access-Control-Allow-Headers "Authorization, X-Requested-With, Content-Type" always; proxy_hide_header Access-Control-Allow-Credentials; add_header Access-Control-Allow-Credentials "true" always; if ($request_method = OPTIONS ) { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods "GET,POST,DELETE,PUT,OPTIONS"; add_header Access-Control-Allow-Headers "Authorization, X-Requested-With, Content-Type"; add_header Access-Control-Allow-Credentials "true" always; add_header Content-Length 0; add_header Content-Type text/plain; add_header Allow GET,POST,DELETE,PUT,OPTIONS; return 200; } proxy_pass http://test_upstream; } }
3. 验证并行效果
修改配置后重启Nginx和PM2,重新运行测试代码,你应该能看到Node日志里多个实例同时处理请求,比如:
0|index | -----> GOT REQUEST -> 1527796135425 1|index | -----> GOT REQUEST -> 1527796135426 2|index | -----> GOT REQUEST -> 1527796135427 3|index | -----> GOT REQUEST -> 1527796135428 0|index | Response time: 1527796135572 1|index | Response time: 1527796135573 ...
也可以用curl快速验证:
for i in {1..10}; do curl http://localhost:8000 & done
三、额外建议
- 你的Express代码本身是无状态的异步处理,只要请求能并行到达,就能充分利用多实例的优势
- 如果后续性能还不够,可以考虑升级到HTTP/2(Nginx和Express都支持),从协议层面解决队头阻塞问题
内容的提问来源于stack exchange,提问作者Patzick
相关产品推荐
相关产品推荐

