openai-compatible-sdk-keepalive · ZH · 2026-10-27

经 OpenAI 兼容端点跑长请求:针对六后端调客户端 socket 超时与 keep-alive

针对聚合站支持的 Claude、GPT、DeepSeek、Qwen、GLM、Kimi 六类后端,从客户端角度梳理如何设置 HTTP socket 读超时、重试策略与 keep-alive 连接复用,避免长请求被过早中断,同时保持连接池健康。

长请求失败,很多时候不是模型算得慢,而是客户端等不及。OpenAI 兼容端点把不同后端统一到同一个协议下,但各后端在实际响应节奏上仍有差异。客户端如果套用一套固定的短超时,就容易在慢后端上丢掉已经生成的回答。

先分清两类超时

客户端与网关之间通常涉及两个阶段的等待:

  • 连接/写超时:建立 TCP/TLS 连接、发送请求体的耗时。这个阶段一般不会太久,可以设得比较紧。
  • socket 读超时:等待服务端返回响应字节的间隔。长请求主要卡在这里,也是本文的重点。

很多 HTTP 客户端把总超时和读超时混在一起。对于流式响应,正确的做法是让总超时足够长甚至不设硬上限,而用读超时来限制“两次数据之间的最大空闲间隔”。这样既能容忍长时间生成,又能在连接真正挂死时及时失败。

逐后端设置读超时

不同后端的生成节奏不同,读超时不宜一刀切。可以按“该后端最慢情况下,两次 token 之间的最长停顿”来估算:

  • Claude、GPT:通常响应较快且流式稳定,读超时可以设得中等,作为基线值。
  • DeepSeek、Qwen、GLM、Kimi:在负载高或长上下文场景下,首字节和中间停顿可能更长,读超时应比基线更宽松。

实践上可以准备两档或三档超时配置,在客户端按模型标识或后端分组选择,而不是给所有请求用同一个值。注意读超时衡量的是字节间隔,不是总时长;不要把总时长限制误当成读超时。

重试要区分“可重试”与“不可重试”

长请求的重试成本高,乱重试会放大后端压力,也可能重复计费。建议:

  • 连接失败、TLS 握手失败、网关返回的 5xx、明确的限流响应:可以重试。
  • 读超时发生在已经收到部分流式数据之后:谨慎处理。此时请求可能已在后端执行,重试意味着重新生成;如果客户端支持续传则优先续传,否则按业务容忍度决定是否重发。
  • 4xx 中的参数错误、鉴权错误:不要重试,重试也不会成功。

重试应带指数退避和抖动,并限制最大次数。对长请求,重试次数建议比短请求更保守。

keep-alive 与连接池

聚合站背后是多个后端,客户端到网关的连接复用对延迟和资源都有影响:

  • 开启 keep-alive:避免每个请求都重新握手,对高频短请求收益明显。
  • 连接池大小要匹配并发:池太小会导致请求排队等待连接,表现为“还没发出去就超时”;池太大则占用过多文件描述符。
  • 空闲连接超时要小于服务端/网关的空闲回收时间:否则客户端可能复用到已被对端关闭的连接,触发一次失败后重连。把客户端的空闲超时设得略短一些,可以让回收由客户端主动完成。
  • 长请求会长期占用连接:如果并发长请求多,要确保连接池上限能覆盖同时在跑的请求数,避免长请求把池占满、短请求饿死。

流式与非流式的差别

  • 流式:读超时按字节间隔判断,适合长生成;客户端要能处理分块到达,并在收到结束标记后才算完成。
  • 非流式:服务端会等全部生成完再返回,客户端在这段时间内收不到任何字节,读超时必须覆盖整个生成时长。若非流式请求也走短读超时,几乎必然失败。对长请求,优先使用流式。

一个可落地的配置思路

  1. 连接超时设短,读超时按后端分档,长请求不设总时长硬上限。
  2. 对已开始接收数据的流,读超时中断视为异常,按业务决定是否重发。
  3. 重试只覆盖连接错误、5xx 和限流,带退避与次数上限。
  4. 开启 keep-alive,客户端空闲超时略短于网关回收时间,连接池上限覆盖并发长请求数。
  5. 上线前用各后端的慢场景做一次压测,校准读超时档位,而不是凭感觉设值。

这些设置不改变计费方式:你仍然按官方价 ×1.3 扣费,贡献 key 按 ×1.1(优质 ×1.2)返 USDC。它们影响的是请求能否完整拿到结果,以及连接资源是否被合理使用。