2.1 长连接网关的分层治理:四层卸载、七层转换与连接池复用 我见过一个团队用单台 Nginx 想撑起百万级流式连接,结果大促当晚 Nginx 进程的 CPU 跑满 100%,worker 全部 busy,新连接排队甚至直接被内核丢弃。他们的第一反应是"加机器",于是把 Nginx 从 1 台扩到 4 台再到 8 台,问题缓解但没根治——因为根子不在机器数量,而在"把所有活都堆给一个组件"。TLS 握手吃 CPU、HTTP 解析吃 CPU、业务路由吃 CPU、流式转发又吃 CPU,全压在一个进程里,任何一环卡住都会拖垮全局。 这一节要把网关层彻底拆开。百万级流式长连接下,网关必须分层,让每一层只做自己最擅长的事,把更重的活卸载到更合适的组件。这是接入层能不能扛住百万长连接的根本。
我见过一个团队用单台 Nginx 想撑起百万级流式连接,结果大促当晚 Nginx 进程的 CPU 跑满 100%,worker 全部 busy,新连接排队甚至直接被内核丢弃。他们的第一反应是"加机器",于是把 Nginx 从 1 台扩到 4 台再到 8 台,问题缓解但没根治——因为根子不在机器数量,而在"把所有活都堆给一个组件"。TLS 握手吃 CPU、HTTP 解析吃 CPU、业务路由吃 CPU、流式转发又吃 CPU,全压在一个进程里,任何一环卡住都会拖垮全局。
这一节要把网关层彻底拆开。百万级流式长连接下,网关必须分层,让每一层只做自己最擅长的事,把更重的活卸载到更合适的组件。这是接入层能不能扛住百万长连接的根本。
低 QPS 场景用一个 Nginx 撑起一切没问题。但百万级流式长连接下,单一组件会成为单点瓶颈。TLS 握手是非对称加密,极度消耗 CPU——百万长连接意味着每秒可能有数十万次 TLS 握手,光这一项就能把一台普通服务器的 CPU 跑满。如果让七层网关也承担 TLS,CPU 先被握手打满,根本没空做业务路由。所以第一层必须把 TLS 卸载掉。
分层之后还有一个隐性收益:故障隔离。当某一层出问题(比如七层网关有 bug 内存泄漏),其它层还能兜一段时间,给你定位和重启的窗口。而单组件架构里,一处崩处处崩,连排查的时间都没有。
正确的做法是职责分层。每一层只承担一个核心职责,层与层之间用清晰的接口契约衔接。下面这张图把高并发大模型对话网关的四层结构画清楚:
这一层在内核态或专用硬件完成两件事:TLS 卸载(终止 HTTPS,把加密解密做完),以及四层负载均衡(按 IP/端口分流,把解密后的裸 TCP 流量分发给后端)。
为什么这一层必须独立?因为百万长连接场景下 TLS 握手是 CPU 杀手。把 TLS 卸载到专门的 L4 层(或带硬件加速的负载均衡),是高并发的第一道省 CPU 杠杆。卸载之后,后端的七层网关拿到的已经是明文流量,专心做协议解析和路由就行。
典型选型有几类。LVS 或 DPDK 做四层负载,前者是 Linux 内核态的经典方案,后者是用户态零拷贝的高吞吐方案,都能扛极高的包速率。云厂商的 SLB/NLB 是托管四层负载,自带 TLS 卸载和 DDoS 防护,省运维,适合不想自己管 LVS 的团队。超大规模或金融级场景会上带 SSL 硬件加速的专用设备。
这一层有几个关键调优点。第一是开启 TLS 会话恢复(Session Resumption 或 0-RTT),让重复连接跳过完整握手,大幅降低长连接重连的成本——大模型对话里连接断开重连很常见,没有会话恢复的话每次重连都要完整握手,CPU 又来一遍。第二是连接迁移,四层负载要支持连接漂移,避免单台后端宕机导致百万连接全部断开重连,形成"重连风暴"。LVS 的连接同步、云 SLB 的健康检查与自动摘除都是为此。
四层卸载之后,进入七层转换层,按 HTTP 协议解析请求,做七层路由(按 Host、Path、Header 分流)、协议转换(HTTP/1 ↔ HTTP/2 ↔ WebSocket)、灰度发布。典型选型是 Nginx、Envoy、APISIX、Kong。
大模型服务场景在这一层有几个特殊的坑,是经典 Web 配置里不会遇到的。
长连接超时要重新设计。 Nginx 默认的 proxy_read_timeout 是 60 秒,经典 Web 完全够用。但一次长上下文流式对话可能持续 2 到 5 分钟,用默认配置,流式输出到一半就会被网关掐断。必须为大模型对话路由单独配置更长的读写超时(比如 300 秒),并且要区分"读超时"和"发送超时"——前者是上游多久没返回数据就断,后者是客户端多久没接收就断。
流式透传要禁用缓冲。 Nginx 默认会缓冲上游响应再转发(proxy_buffering on),这在经典 Web 是优化(减少上游连接占用),但在流式场景会让 TTFT 飙升——网关把整个响应收完才转发,用户看到的是"等了几十秒后一次性蹦出全部内容",而不是流式打字机效果。必须设置 proxy_buffering off 和 proxy_request_buffering off,让 token 一边来一边吐。我曾经帮一个团队排查 TTFT 异常,最后发现就是这行配置,改完 TTFT 从 12 秒掉到 1 秒以内。
HTTP/2 和 HTTP/3 的多路复用。 能显著降低连接数压力,但要注意 HTTP/2 的并发流上限和队头阻塞(HOL)在流式场景的微妙影响——一个慢请求可能阻塞同一连接上的其它请求。生产里通常会做连接级隔离,关键请求走独立连接。
七层之后进入应用网关,这里承载鉴权、限流、风控、WAF、请求改写、会话亲和路由等业务逻辑。这是真正"理解大模型对话"的一层,也是自研比例最高的一层。
这一层要做的事包括:鉴权与配额,校验 token、按用户/租户查配额、按会员等级定优先级;会话亲和路由,根据 session_id 把请求路由到持有其上下文的推理节点(这是 1.3 反模式一的修正);分层限流与过载保护(2.2、2.3 节);WAF 与输入过滤,拦截注入攻击、敏感词(本系列有专门的输入输出过滤教程)。
选型上有两条路。要么基于 Go/Netty/asyncio 自研核心路径,把鉴权、亲和路由、限流这些性能敏感的逻辑做到极致;要么用 API 网关(APISIX/Kong + 插件)做边缘功能。百万级场景大多是"自研核心路径 + 网关做边缘功能"的混合——性能瓶颈在自研层解决,标准化功能(OAuth、限流插件)交给成熟网关。
这一层管理应用网关到推理集群(vLLM/TGI 节点)的连接。它解决的问题是:如果网关到推理用"一请求一连接",高并发下推理节点会被连接洪流淹没,握手开销和文件描述符都会爆。连接池让网关与推理之间维持少量长连接,请求在连接上多路复用。
关键设计点有三个。每节点的连接数上限,根据推理节点的并发能力设定,避免压垮下游——一个 vLLM 节点能同时处理 256 个请求,你的连接池就该控制在 256 以内,多出来的请求在网关层排队而不是硬塞给推理。健康检查与故障摘除,池里的连接要定期探活,故障节点及时摘除,否则请求会被分发到死节点然后超时。连接预热,高峰前预先建好连接池,避免冷启动时的握手风暴——大促开场瞬间如果有 10 万新请求同时建连,TLS 握手就能把网关打瘫,预热是必须的。
分层治理的最后一环,是背压机制。这是流式场景特有的、且极其关键的稳定性保障,很多团队忽视了它,结果过载时系统不是优雅减速而是突然崩塌。
背压的核心是:当推理集群处理不过来时(队列堆积、显存紧张),它要能向网关"反压"——告诉网关"我慢了,你别发这么快"。网关再向客户端反压(减慢读取、提示等待),形成一条从 GPU 一路传导到客户端的压力链。没有背压会怎样?推理集群被压垮(OOM、超时),网关还在疯狂往里塞请求,最终全盘雪崩。背压让系统在过载时优雅减速而非突然崩塌。
实现背压有几个要点。流式响应的窗口控制,网关根据下游消费速度控制从推理读取的速度,避免内存堆积——这类似 TCP 的滑动窗口。队列长度阈值,推理队列超过阈值时向网关返回"慢一点"信号(HTTP 429 带 Retry-After,或自定义背压头部)。客户端侧的退避,通过 SSE 心跳或显式的"正在思考"提示,让用户感知系统在有序工作而非卡死。背压做好了,过载时用户感知到的是"变慢了",而不是"挂了"。
把这四层一层职责梳理一遍:四层卸载管 TLS 和裸流量分流,七层转换管协议解析和路由,应用网关管业务逻辑和亲和路由,连接池管到推理的连接复用,再加上贯穿全链路的背压。每一层都可以独立扩缩、独立排障。我强烈建议你在自己的架构图上把这四层明确画出来,并且在每一层标注它当前用的什么组件、容量是多少、瓶颈点在哪——这样下次故障时,你能立刻定位是哪一层的问题,而不是在一张糊成一团的架构图前抓瞎。
下一节 2.2,我们深入网关里最核心的算法问题——限流的三种算法与它们的组合之道。