PAYATHON 2026

客户端 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,可以按下面的步骤修改:

  1. 打开 IIS Manager。
  2. 选中服务器节点,不要选择具体网站。
  3. 打开 Application Request Routing Cache。
  4. 点击右侧的 Server Proxy Settings。
  5. 将 Time-out (seconds) 从 120 改为需要的值,例如 900。
  6. 应用配置,然后重启相关站点或应用程序池。

对应的服务器级配置通常如下:

<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 请求一直保持连接。用户可能停留数分钟,代理、浏览器、移动网络或服务器也可能在此期间回收连接。

更可靠的流程如下:

  1. 前端创建支付任务。
  2. 服务端立即返回 paymentId 和当前状态。
  3. 支付引擎处理完成后调用服务端 webhook。
  4. 服务端持久化支付状态。
  5. 前端定期查询状态,或者通过 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 请求承载整个支付过程。

备注:内容仅供参考。