Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

[Bug] AI Gateway Fails to Reconnect After Upstream Connection Becomes Unresponsive

オープン
#14,129 コメント 1 件 リアクション 0 件 担当者 1 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@wanghe-fit2cloud がすでに取り組んでいます。

2026年10月19日 から。

評価

この issue はまだ評価されていません。

説明

type: ai-gateway type: bug
Contact Information

No response

1Panel Version

v2.1.13(Kylin Linux Advanced Server V10,Docker 部署)

AI Gateway 应用版本:v1.2.0
镜像:1panel/ai-gateway@sha256:8a5ada1094609c281f6a99383339e37b4958e4a2d26eb9921f1d8dd1b8283792

Problem Description

AI Gateway 在一条上游 HTTP/2 长连接失效后,会持续复用这条已不可用的连接,导致所有模型、所有协议的请求一起超时;直到 Linux 内核 TCP 重传耗尽(约 16 分钟)或重启容器后才恢复。

环境与配置

  • 1Panel:v2.1.13,Kylin Linux Advanced Server V10
  • AI Gateway:v1.2.0,Docker 部署
  • 单个上游账号:DeepSeek,https://api.deepseek.com
  • 同时启用三种协议:
    • openaiChatCompletions → /chat/completions
    • openaiResponses → /responses
    • anthropicMessages → /anthropic/v1/messages
  • 模型映射:deepseek-flash、deepseek-v4-flash、deepseek-v4-pro → deepseek-flash

影响

2026-10-19 当天出现两次(CST):

  • 第一次约 15:35 开始,重启 1Panel-ai-gateway-***** 容器后恢复;
  • 第二次 17:44 开始,未重启,17:59:52 自行恢复。

故障期间容器状态 healthy,CPU 0.12%、内存 55.8MiB,进程未崩溃、无重启;但三种协议的所有推理请求都卡住或超时。新建上游连接完全正常,说明不是密钥、模型名或协议转换问题。

关键证据

  1. 故障期间容器内只有一条上游连接,且已无 ACK、无新数据:
$ nsenter -t <gateway-pid> -n ss -ntip dst :443
ESTAB 0 522781 172.18.0.11:45450 171.105.220.186:443
  cubic rto:120000 backoff:15 cwnd:1
  bytes_sent:4474356 bytes_acked:4451789
  lastrcv:895584 lastack:895580
  unacked:11 retrans:1/20 lost:11
  notsent:508248

Send-Q 积压约 511KB,lastrcv / lastack 已停滞约 895 秒,RTO 退避到 120 秒;该连接持续重传,但收不到任何 ACK 或数据。

  1. 同一时间、同一容器网络内,新建连接访问 api.deepseek.com 解析出的 4 个 IP(包括故障连接的对端 171.105.220.186)全部正常:
171.105.220.186  HTTP/2 200  first=0.023s total=0.647s
171.108.213.114 HTTP/2 200  first=0.021s total=0.787s
171.108.215.12  HTTP/2 200  first=0.023s total=0.714s
171.108.216.18  HTTP/2 200  first=0.023s total=0.725s

HTTP/1.1 也同样正常,说明上游服务、DNS 和出网链路对新连接均可用,故障只存在于被复用的旧 TCP 流。

  1. 旧连接消失后网关立即恢复:
17:59:34  旧连接仍在,Send-Q 522781,请求超时
17:59:52  旧连接消失(conns=0)
17:59:52  下一次请求 HTTP 200,0.76 秒
          新连接 172.18.0.11:40516 -> 171.105.220.186:443(fd=17)

未重启容器、未修改配置,仅因为内核最终放弃旧 TCP 连接,网关就自动新建连接并恢复。

  1. 时间线:
时间(CST) 事件
17:43:57 最后一次正常收到上游数据
17:43:59 首个失败请求,120 秒后报 upstream_unavailable
17:44–17:59 后续请求全部超时或被客户端取消
17:59:52 旧连接被内核拆除,下一次请求立即 200

从最后一次收到数据到恢复约 15 分 55 秒,与 tcp_retries2=15、RTO 上限 120 秒的表现吻合。

  1. 相关配置:
gateway.request_timeout_seconds = 120
gateway.stream_idle_timeout_seconds = 120
gateway.max_concurrency = 10

net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_retries2 = 15

网关设置中未发现 HTTP/2 保活相关配置项;从实际表现看,120 秒的请求超时并没有触发连接关闭或重建。

  1. 网关日志:
time=2026-10-19T09:45:59.796Z level=WARN
msg="upstream attempt failed"
request_id=608f6e0f90f684016bea78d717dec2ae
stage=send_attempt reason=timeout account_id=1 request_written=true

除该 WARN 外,没有 panic、goroutine 堆栈或连接重建日志。

根因分析(推测)

  1. 17:44 左右,一条到上游的 TCP 流进入黑洞状态:没有 ACK、没有新数据,但连接仍保持 ESTAB。
  2. 网关没有及时识别这条共享连接已不可用,后续所有请求继续复用它。
  3. request_timeout / stream_idle_timeout 只结束单次请求,不会关闭底层连接;TCP keepalive 为默认 7200 秒,HTTP/2 保活疑似未启用。
  4. 最终只能等 Linux 按 tcp_retries2=15 放弃 TCP 连接,导致单次网络异常放大为约 16 分钟的全局不可用。
  5. 当前只有一个上游账号,三种协议共用同一个 host 连接池,因此一条死连接会影响所有模型和所有协议。

需要说明:上游 TCP 流为何在 17:44 变成黑洞,现有证据无法确定是上游侧还是中间网络触发;但网关在连接失效后的恢复策略不够快,这是可以改进的部分。

Steps to Reproduce

该问题为偶发,生产环境未主动执行破坏性复现。建议在测试环境用以下方式稳定复现:

  1. 部署 AI Gateway v1.2.0,配置一个 DeepSeek 上游账号及上述三种协议;
  2. 发起一次正常请求,确认上游连接建立;
  3. 在容器网络命名空间中找到该连接的 4 元组;
  4. 只对该 4 元组临时下发 DROP 规则(仅测试环境),模拟 TCP 黑洞;
  5. 继续发起请求,观察网关是否持续复用旧连接、是否在 120 秒超时后重建;
  6. 预期可复现“新连接正常、旧连接上的所有请求持续超时,直到 TCP 重传耗尽或容器重启”。
The expected correct result
  1. 上游请求超时(尤其是 send_attempt / request_written=true 后无响应)时,应将对应连接标记为不可用并主动关闭,而不是继续放回连接池;
  2. 为上游 HTTP/2 连接启用保活(如 ReadIdleTimeout / PingTimeout),或设置合理的 TCP_USER_TIMEOUT,让失效连接在几十秒内被发现;
  3. 连接失效后,新请求应能自动新建连接;对幂等请求可考虑在新连接上重试一次;
  4. 增加“连接被丢弃/重建”的日志,便于定位此类问题;
  5. 如果存在多个上游账号,建议支持连接级故障转移。
Related log output
time=2026-10-19T09:45:59.796Z level=WARN msg="upstream attempt failed" request_id=608f6e0f90f684016bea78d717dec2ae stage=send_attempt reason=timeout account_id=1 request_written=true

调用日志摘要:

17:43:59  deepseek-flash  duration=120084ms  error=upstream_unavailable
17:46:00  deepseek-flash  duration=500198ms  error=request_canceled
17:51:01  deepseek-flash  duration=29989ms   error=request_canceled
17:51:03  deepseek-flash  duration=199735ms  error=request_canceled
17:55:33  deepseek-flash  duration=11718ms   error=request_canceled
Additional Information
  • 容器未暴露 pprof / 调试端口,故障期间未能采集 goroutine 堆栈;以上判断基于 TCP 状态、调用日志和恢复过程。
  • 已检索公开 issue,未发现与本问题完全一致的记录;#14035 是高并发 503,#13965 是双认证头 401,均不能解释本次“旧连接卡死、新连接正常、重启或连接拆除后立即恢复”的现象。
  • 当前运行 v1.2.0;v1.4.0 发布说明未明确提到该连接复用/失效重建问题,升级后我们会继续验证并补充结果。
  • 以上日志和 IP 已做脱敏,未包含密钥;如需更多现场数据可以继续提供。
主要言語
Go
スター
37.2k
フォーク
3.4k
平均マージ
11時間 43分
マージ済み PR(30日)
84

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

1Panel-dev/1Panel のほかの issue

1Panel-dev/1Panel の issue をすべて見る

似ている issue

Go の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。