3.5 KiB
Raw Blame History

Context

现有普通 Responses 流式已经具备较完整的断连续写能力:客户端写失败后可继续 drain 上游,并且具备数据间隔超时和 keepalive。图片流式路径目前仍然采用更直接的读写方式客户端写失败会立即返回上游读取也更容易跟随客户端取消而结束。

本次变更只针对图片流式路径,不改变普通文本流式路径的配置和行为。系统已经存在普通流式的后端超时配置,因此这里不引入页面级超时设置;图片流式只需要独立的后端默认值,让图片生成有更长的容忍窗口。

Goals / Non-Goals

Goals:

  • 图片流式在客户端断开后继续读取上游,尽量保留最终图片结果与计费结果。
  • 图片流式使用独立于普通流式的超时与 keepalive 默认值。
  • 不修改现有普通流式配置项的含义,不要求管理员新增页面配置。
  • 维持图片计费与图片结果计数的一致性。

Non-Goals:

  • 不设计新的前端配置页面。
  • 不修改普通文本流式的超时策略。
  • 不改变图片计费公式或分组倍率语义。

Decisions

  1. 使用独立的图片流式配置键

    • 选择:在后端配置中增加图片流式专用 image_stream_data_interval_timeout / image_stream_keepalive_interval
    • 原因:图片流式耗时显著更长,复用普通流式默认值会过早触发超时;独立键能避免影响现有文本流式。
    • 备选方案:直接复用普通流式配置并在代码里按路径放大倍数。这个方案会让普通流式和图片流式共享语义,后续难以维护。
  2. 继续使用上下文 detach而不是依赖客户端上下文

    • 选择:图片流式请求向上游发起时使用 context.WithoutCancel 派生的上下文。
    • 原因:客户端断开时不应自动取消上游请求,否则无法收集最终图片结果,也无法完成图片计费。
    • 备选方案:仍使用 c.Request.Context() 并只在写失败后继续 drain。这个方案在客户端取消场景下无法保证上游读取继续进行。
  3. 只改图片流式路径,不改普通流式路径

    • 选择:/v1/images/*Responses + image_generation 两条图片流式链路单独处理。
    • 原因:风险最小,避免回归普通文本流式和现有超时配置。
    • 备选方案:统一重构所有流式处理。这个方案范围更大,验证成本更高,不符合本次“尽量少改现有行为”的目标。
  4. 不新增页面配置

    • 选择:图片流式独立超时默认值写入后端配置,沿用当前配置加载方式。
    • 原因:用户明确要求和当前设置行为统一,不需要额外页面输入项。
    • 备选方案:前端增加图片超时配置项。这个方案会改变现有运维方式,也容易引入误配。

Risks / Trade-offs

  • [Risk] 图片流式继续 drain 上游后,客户端已经断开但服务端仍占用连接与协程资源。→ [Mitigation] 只对图片流式启用更长但仍有限的专用超时,并保持与普通流式同样的 keepalive/超时退出机制。
  • [Risk] 图片流式与普通流式的默认超时不同,运维如果只关注通用配置可能忽略图片专用值。→ [Mitigation] 在配置示例中明确标注图片流式专用默认值和用途。
  • [Risk] 断连后继续读取可能导致日志中出现“客户端断开但最终成功”的状态。→ [Mitigation] 保留现有图片计费结果返回语义,同时让调用方在结果与错误并存时优先使用结果对象。