客户端 GET 请求 2 分钟后出现 502,如何延长 timeout?
结论
请求在 2 分钟后返回 502,通常不是 axios 或 Node.js 主动中止,而是 IIS 的反向代理组件等待上游响应超时。这里常见的组件是 ARR(Application Request Routing)。
IIS 站点的 Connection Timeout 主要控制连接的空闲时间,并不是反向代理等待 Node.js 响应的时间。需要调整的是 ARR 的 Proxy Timeout。
不过,让一个 HTTP 请求持续等待十几分钟,直到用户完成支付,并不可靠。更稳妥的做法是立即返回支付任务 ID,由支付平台通过 webhook 更新状态,前端再轮询查询结果。
调整 IIS ARR 的代理超时
如果 IIS 通过 URL Rewrite 和 ARR 将请求转发到 Node.js,可以按下面的步骤修改:
- 打开 IIS Manager。
- 选中服务器节点,不要选择具体网站。
- 打开 Application Request Routing Cache。
- 点击右侧的 Server Proxy Settings。
- 将 Time-out (seconds) 从
120改为需要的值,例如900。 - 应用配置,然后重启相关站点或应用程序池。
对应的服务器级配置通常如下:
<configuration>
<system.webServer>
<proxy enabled="true" timeout="00:15:00" />
</system.webServer>
</configuration>
这项配置一般位于 IIS 服务器级的 applicationHost.config 中。能否在站点的 web.config 中设置,取决于 IIS 是否已经解锁对应的配置节。如果配置节处于锁定状态,将内容写入 web.config 后可能不会生效,也可能直接引发配置错误。
URL Rewrite 的转发规则只负责决定请求转发到哪里,例如:
<system.webServer>
<rewrite>
<rules>
<rule name="ReverseProxy" stopProcessing="true">
<match url="(.*)" />
<action
type="Rewrite"
url="http://127.0.0.1:3000/{R:1}"
/>
</rule>
</rules>
</rewrite>
</system.webServer>
这类规则本身通常无法解决 ARR 的 120 秒代理超时。
检查 Axios 超时
浏览器端还需要确认代码中没有显式设置 timeout: 120000。常见版本的 Axios 默认超时通常是 0,也就是 Axios 不主动设置超时,但项目中的 Axios 实例或拦截器可能会覆盖这个值。
const api = axios.create({
baseURL: "/api",
timeout: 15 * 60 * 1000
});
const response = await api.get("/payment/result");
也可以为单次请求设置超时:
const response = await axios.get("/api/payment/result", {
timeout: 15 * 60 * 1000
});
如果错误响应来自 IIS,那么延长 Axios 超时时间也无法绕过 ARR 的代理超时。
检查 Node.js HTTP Server
Node.js 也可能设置了自己的超时限制。相关属性和默认行为会随 Node.js 版本变化,需要根据当前运行版本确认。
import http from "node:http";
import app from "./app.js";
const server = http.createServer(app);
server.requestTimeout = 15 * 60 * 1000;
server.timeout = 15 * 60 * 1000;
server.listen(3000);
如果使用 Express:
const server = app.listen(3000, () => {
console.log("Server listening on port 3000");
});
server.requestTimeout = 15 * 60 * 1000;
server.timeout = 15 * 60 * 1000;
请求经过 IIS 时,整条链路中超时时间最短的那一层决定了实际能够等待多久:
浏览器/Axios
→ CDN、WAF 或负载均衡器
→ IIS ARR
→ Node.js
→ 支付服务
只修改 Node.js 的设置,无法阻止 IIS 在 120 秒时返回 502。如果 IIS 前面还有 CDN、云负载均衡器或安全网关,还要检查这些组件的 upstream、idle 或 request timeout。
推荐的支付结果处理方式
支付过程中可能包含填写表单、验证或页面跳转,不适合让最初的 GET 请求一直保持连接。用户可能停留数分钟,代理、浏览器、移动网络或服务器也可能在此期间回收连接。
更可靠的流程如下:
- 前端创建支付任务。
- 服务端立即返回
paymentId和当前状态。 - 支付引擎处理完成后调用服务端 webhook。
- 服务端持久化支付状态。
- 前端定期查询状态,或者通过 SSE/WebSocket 接收更新。
创建支付任务:
app.post("/api/payments", async (req, res) => {
const payment = await createPayment(req.body);
res.status(202).json({
paymentId: payment.id,
status: "pending"
});
});
接收支付回调:
app.post("/api/payment-callback", async (req, res) => {
await updatePaymentStatus(req.body.paymentId, req.body.status);
res.sendStatus(200);
});
查询支付状态:
app.get("/api/payments/:paymentId", async (req, res) => {
const payment = await findPayment(req.params.paymentId);
if (!payment) {
return res.sendStatus(404);
}
res.json({
paymentId: payment.id,
status: payment.status,
result: payment.result
});
});
前端轮询:
async function waitForPayment(paymentId) {
const deadline = Date.now() + 15 * 60 * 1000;
while (Date.now() < deadline) {
const { data } = await axios.get(`/api/payments/${paymentId}`);
if (data.status === "completed") {
return data.result;
}
if (data.status === "failed") {
throw new Error("Payment failed");
}
await new Promise(resolve => setTimeout(resolve, 3000));
}
throw new Error("Payment status check timed out");
}
支付状态应保存在数据库、缓存或其他持久化存储中,不宜只依赖进程内的 EventEmitter。Node.js 进程重启后,内存事件可能丢失。在多实例部署或负载均衡环境中,回调也可能被另一个实例接收。
排查时重点确认
先查看 IIS 日志中的子状态码。502.3 通常表示反向代理在连接上游或等待上游响应时超时,但还需要结合 IIS 日志、Failed Request Tracing 和 ARR 日志进一步确认。
同时检查以下设置:
- IIS ARR 的 Time-out (seconds)。
- Axios 实例或单次请求是否设置了
120000。 - Node.js 的
requestTimeout、timeout,以及所用框架的超时配置。 - CDN、WAF、负载均衡器和云网关的空闲超时。
- 支付引擎的 callback 是否确实到达当前 Node.js 实例。
- 响应是否已经开始发送,连接是否因为长时间没有数据而被中间代理判定为空闲。
短期可以把 ARR Proxy Timeout 调整为 900 秒,同时检查链路中其他组件的超时设置。长期应将支付流程改为异步状态查询或推送,避免让一个持续十几分钟的 GET 请求承载整个支付过程。
备注:内容仅供参考。
