☰
大模型网关避坑实录:从Cloudflare 100秒超时到OCI L7负载均衡器迁移
2026/10/1 8:54:12 网站建设 项目流程

一直做网关的人,最怕收到的反馈大概就是“你的服务挂了”。CPU和内存明明还有大量富余,日志也一直在正常滚动,但访问的人就是打不开。我那个负责转发大模型请求的网关,就曾经长期处于这种状态。折腾到最后才意识到,真正卡脖子的不是模型推理本身,而是暴露服务之前绕不开的三件小事:域名落到哪、端口能不能通、链路等多久就断。这篇文章把我这段从 Cloudflare 撞墙,一直到迁移到 OCI 免费 L7 云负载均衡器的心路历程完整摊开。适合正在自建大模型网关、在公司内网做 AI 服务接入,以及被各种 443 连接超时折磨过的人参考——涉及的超时原理、端口取舍和排障手法,都是可以直接抄作业的。

1. 大模型网关的部署难题,远不止“服务跑起来”那么简单

1.1 网关的日常职责,注定了它会被网络问题反复摩擦

大模型网关在团队内部承担的职责非常杂。它要对上统一暴露一个 OpenAI 兼容的 API 出口,把不同模型的请求路径统一起来;要对下做 API Key 鉴权、用户配额限制、请求日志审计;还要负责把同一个提问路由到不同的模型或者不同的供应商后端。

听起来就是一个标准的 API 网关,但问题是,大模型接口的生命周期跟普通 REST API 完全不是一个量级。普通接口请求 500 毫秒内能返回就算慢的,大模型接口从用户提问到完整回答,动不动就是几十秒起步。再加上现在很多模型走流式输出,用 SSE 一点一点把 Token 吐给前端,整个连接要维持很久,中间还有“模型思考过程”这种完全不产生数据的空窗期。

这种长时间、大流量、长连接的访问模式,对任何一层网络设备的要求都比普通 Web 服务高得多。一开始团队图省事,直接把网关端口映射到公网 IP 上,让同事用http://IP:8000访问。当天就得罪了所有同事——有人单位的网络策略禁止访问非标准端口;有人客户端直接报证书错误;更别提部署那台机器的 IP 地址在一个月内因为云厂商维护变了两次,所有端到端配置全废。

1.2 域名、端口、超时,三个问题其实是同一件事

很多人把域名、端口、超时拆开来看,觉得都是独立的小问题,但实际部署网关的时候,它们是一个链条:

  • 域名决定了流量到达哪个“门面”。公网 IP 不是不能访问,但 IP 一旦更换就要通知所有人,而且没有证书的 HTTP 请求在 TLS 时代寸步难行。
  • 端口决定了流量能不能穿透用户侧的网络策略。80 和 443 是互联网默认放行的端口,其他端口属于“允许临时用,但随时可能被掐”的状态。
  • 超时决定了这条链路上的每一层设备能容忍你多久不出数据。普通网络中间的 NAT 网关、负载均衡器、反向代理,每一层都有自己的空闲超时和响应超时,超过就会被默默断开。

换句话说,大模型网关要稳定跑起来,需要的是“域名解析稳定 + 标准端口暴露 + 超时完全可控”三合一的环境。缺任何一个,你都会遇到“为什么连得上但总是断”“为什么首 Token 等了几分钟就被掐断”这类玄学问题。

2. Cloudflare 方案看上去很美,但免费层的硬限制是绕不过去的坎

最早把目标锁定在 Cloudflare 上,原因很简单:免费 CDN、免费 HTTPS、隐藏源站 IP、全球节点加速,还能用 Cloudflare Tunnel 让没有公网 IP 的机器直接发布服务。对于个人项目和中小团队来说,这几乎是零成本暴露服务的最优解,尤其是不想再折腾云厂商安全组、防火墙入站规则的时候,Tunnel 一个出站连接就把所有问题解决了。

但我很快就在实际流量压力下发现了三个无法忽视的问题,最后一一变成了阻碍网关落地的硬约束。

2.1 Cloudflare 100 秒限制是如何卡死大模型网关的

Cloudflare 免费版在通过 CDN 代理转发 HTTP 请求时,对回源连接有严格的超时限制。官方文档里写得很明确,免费套餐下请求的整体响应时间上限是 100 秒。也就是说,不管你的后端用了多少时间处理,只要从请求发起到完成响应超过这个值,连接就会被平台直接切断。

对大模型网关来说,这意味着什么?我先用一个最简单的场景做测试:让网关转发一个支持流式输出的聊天请求,后端模型在真正开始吐字之前,需要先经历一段较长的“思考”阶段。在测试里,后端第 40 秒才输出第一个 Token,第 130 秒才全部输出完。结果就是客户端在第 100 秒整收到一个502或者直接被断开,模型还没开始说话,连接就已经没了。

对比数据很直观:

测试项直连后端经过 Cloudflare 代理
首 Token 耗时40 秒40 秒
完整响应耗时130 秒100 秒被强制断开
TTFB(到达首字节时间)正常受边缘链路影响,增加 1~3 秒

100 秒限制对大模型场景几乎是致命的。长上下文处理、RAG 检索、多模型工具调用,这些场景动辄几十秒甚至几分钟的耗时,是没有办法压缩到 100 秒以内的。就算你把后端硬调到百秒内返回,一遇到高峰期排队,照样会被切断。

还有一个很多人不知道的点:Cloudflare Tunnel 虽然绕过了 Worker 的 CPU 时间限制,但它本质上还是走 CDN 边缘代理回源,所以同样受这 100 秒的整体响应超时约束。我当时一度以为换 Tunnel 能解决,测下来发现该断还是断,只是报错方式从 502 变成了连接重置。

2.2 为什么只剩下 443 这个端口可以选

Cloudflare 免费层在对外暴露服务的时候,支持的端口非常有限:面向公网只开放 80 和 443。80 用于 HTTP,443 用于 HTTPS,其他所有端口几乎都不在免费套餐的允许范围内。像大模型网关常用的 gRPC 接口、WebSocket 长连接、自定义 RPC 端口,在 Cloudflare 免费版里都没有办法直接暴露出去。

当时我能做的,就是所谓的“端口折中”:把所有原本想走独立端口的能力,全部硬塞进 443 端口,用 URL 路径去区分不同服务。

比如网关原来的设计里,内置了一个/metrics监控接口和一个/healthz健康检查接口,这些还好说,跟着主路径走就行。但团队里另外一个实时语音服务原本走的是独立端口,非要用自定义协议,我就只能让客户端连到 443,再在路径上做一层协议分发。从客户端视角看,所有请求都指向同一个域名同一个端口,借用的是 HTTPS 本身的相对安全性,但代价是所有服务都暴露在同一个攻击面上,一旦某个路径的鉴权出问题,整个网关注入就会被放大到全局。

端口折中并非不能跑,只是非常别扭。对于个人 demo 项目可以接受,对于要上生产的大模型网关,这是一种“看起来很美好但处处受限”的状态。

2.3 Tunnel 的体验:能用,但离“稳定”还很远

Cloudflare Tunnel 有一个非常大的优势:不需要在公网开放任何入站端口。只要在内网机器上运行一个cloudflared进程,它主动向外建立一条加密通道,外网请求经过 Cloudflare 边缘节点进入这条隧道,就能访问到你的服务。这个机制对没有公网 IP 的家庭服务器、公司内网机器来说特别合适。

但在大模型网关这个场景下,我测下来有几个很明显的问题:

  • 链路稳定性不可控:Tunnel 的边缘节点选择你无法干预,有些地区连接 Tunnel 节点的延迟高得离谱,网关的 TTFB 经常被拖到 3~5 秒,用户体感上就是“点一下要转半天”。
  • 没有合理的健康检查:Cloudflare Tunnel 本身不提供后端健康检查,后端挂了它仍然继续转发,客户端拿到的错误永远是“WebSocket 连接失败”或“404”,而不是一个明确的“服务不可用”。
  • 多路由复用容易冲突:同一个 Tunnel 要转发多个域名、多个路径时,配置稍有不慎就会产生路由重叠。我遇到过/api路径被另一个服务的规则抢先匹配,结果网关的登录请求全被转发到错误后端。

这套方案最后被我定位为“只适合低频、个人使用、无需强 SLA 的场景”。我并不是否定 Cloudflare Tunnel,它解决了许多“没有公网 IP 怎么暴露服务”的困境,但它的设计定位就不是给大流量、长时间连接的生产网关用的。

3. 上 OCI 免费 L7 云负载均衡器,才算真正把主动权握在手里

经过 Cloudflare 一轮折腾之后,我对“免费方案”的要求变成了:端口我可以自定义、超时我可以调参、后端我可以做健康检查、流量我可以按域名路径去路由。这时候瞄上了 Oracle Cloud(OCI)的免费 L7 负载均衡器。它属于 Always Free 资源,只要注册账号,不需要付费也能长期使用。实测下来,这是目前白嫖级别方案里最适合大模型网关上生产的。

3.1 免费 L7 负载均衡器与 Cloudflare 方案的差异

先放一张对比表,每一条都是实际踩过坑后的体感:

对比维度Cloudflare 免费版 + TunnelOCI 免费 L7 负载均衡器
成本免费免费(Always Free)
对外端口仅 80/443L7 监听器可按需配置,比如 443、8443、8080
响应超时固定约 100 秒可配置,空闲超时、响应超时可放宽到 300 秒以上
健康检查无支持 HTTP/TCP 健康检查,自动摘除异常后端
路由规则仅 URL 路径支持域名 + 路径 + 权重灰度
TLS 终结边缘节点终结LB 上统一终结,可上传自己的证书
多可用区高可用依赖 CF 节点默认双可用区部署

总结成一句话:Cloudflare 免费版给你的是一个“不可调参的免费代理”,而 OCI L7 负载均衡器给你的是一个“可自由调参的免费入口”。

这里需要重点说下超时的问题。大模型网关最怕的不是“响应慢”,而是“响应慢到一半被切断”。OCI L7 LB 对连接的管理方式是可控的,监听器上可以调节空闲超时(Idle Timeout)和对应后端连接的超时参数。你可以按自己网关的实际需要,把超时值调整到足够大,而不是被某个隐藏在平台深处的固定常量掐住脖子。

3.2 创建 LB 的完整实操:从 VCN 到监听器

我在 OCI 的部署过程不算复杂,但有几个环节踩了坑,把正确的步骤整理一下。

第一步:准备 VCN 与子网

登录 OCI 控制台,进入“Networking -> Virtual cloud networks”创建一个 VCN。创建时顺手添加一个公共子网,用于挂载负载均衡器的公网入口。要注意的是,LB 和后端网关实例无论是否在同一个可用区,至少要在同一个 VCN 内,否则网络 ACL 和安全列表的配置会非常绕。

第二步:创建负载均衡器

在“Networking -> Load balancers”页面创建 LB。选择 Layer 7 类型,形状选择“Always Free”对应的规格。创建的时候,控制台会要求你选择两个可用区来提供高可用,这是免费资源的一部分,不需要额外付费。策略同区域唯一需要注意的是,LB 创建完之后会分配一个公网 IP,这个 IP 是 VIP,删除 LB 才会回收,不会因为实例重启而变化。

第三步:配置监听器

监听器是面向客户端的入口。这里是关键:

  • 协议选择 HTTPS,端口 443。
  • 如果第一次搭,建议先用 HTTP + 80 把链路测通,再切换 HTTPS,减少排障变量。
  • 在监听器上绑定 TLS 证书,证书可以用腾讯/阿里云/Let's Encrypt 签发的,后面详说。

第四步:创建后端集

后端集就是一组实际处理请求的网关实例。协议选 HTTP,端口填你网关实际监听的端口,比如8000。这里有个非常反直觉的坑:很多人的网关在云主机上监听的是 8000,但健康检查默认走 80 端口,导致健康检查一直失败,LB 把所有后端都标记为 Down。正确做法是在后端集的“健康检查”配置里,把端口改为 8000,路径设为网关的/v1/models或/healthz,并配置期望状态码是 200(有些网关对根路径返回 404,那就把期望码改成 200 和 404)。

第五步:配置路由规则

OCI L7 LB 的路由规则非常灵活。可以按域名路由,也可以按 URL 路径路由,还可以设置权重灰度。大模型网关最典型的用法是:

# 逻辑示意,实际在控制台配置 # Host 为 ai.example.com 的请求转发到 backend-ai # Host 为 metrics.example.com 的请求转发到 backend-metrics

我实际将生产流量、测试流量和监控流量分别放在三个后端集里,每次发版先切少量流量到新版本,验证稳定后再把权重拉满。这在 Cloudflare 免费版里是做不到的。

3.3 证书上传与 TLS 终结的调优

如果监听器选用了 HTTPS,证书是必须配置的。我用的方案是 Let's Encrypt 免费证书,在源站主机上用 certbot 签发。签发一次有效期 90 天,通过在主机上挂 cron 定期执行续期脚本,续期完成后使用 OCI CLI 把新证书上传到 LB:

# 将新证书上传到 LB 的示例命令(命令参数以实际 CLI 版本为准) oci lb certificate create \ --load-balancer-id <lb-ocid> \ --certificate-name "cert-2025" \ --certificate-file /etc/letsencrypt/live/ai.example.com/fullchain.pem \ --private-key-file /etc/letsencrypt/live/ai.example.com/privkey.pem

这里再强调一点:如果使用了 HTTPS 监听器,默认情况下客户端到 LB 之间是加密的,LB 到后端可以用 HTTP。不建议在 LB 到后端之间再开 TLS,因为没必要增加一层开销,反而会引入证书信任问题,排障时多一个变量。

安全配置方面,建议禁用 TLS 1.0 和 TLS 1.1,只开启 TLS 1.2 和 TLS 1.3。HTTP/2 可以直接在监听器上启用,对 SSE 的头部压缩和多路复用都有好处。

3.4 超时参数与长连接优化的实操清单

大模型网关绕不开的就是 SSE 流式输出。为了让流式连接在 LB 上稳定不中断,我做了一组配套调优:

  • LB 监听器的空闲超时:从默认值调高到 300 秒。如果模型思考时间特别长,就再上调到 600 秒。
  • 后端的 keep-alive:网关程序(比如 FastAPI 或 Node.js)要确保开启 HTTP keep-alive,避免频繁重建 TCP 连接。
  • 反向代理层透传:如果网关前面还有一层 Nginx,必须把proxy_read_timeout、proxy_send_timeout都设置为 300 秒以上,并且设置proxy_buffering off,否则流式输出会被缓冲层憋住。
  • SSE 心跳:在后端代码里,每 15~20 秒在没有实际 Token 数据时,发送一个以冒号开头的注释行(": keep-alive\n\n")。这行注释在 SSE 协议里等价于心跳,能有效防止中间 NAT 设备因为“长时间无流量”而断开连接。

具体的快速验证命令如下。先用直连 IP 测后端:

curl -v --max-time 60 http://10.0.0.4:8000/v1/models

再通过 LB 测完整链路:

curl -v https://ai.example.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-xxx" \ -d '{"model":"deepseek-chat","stream":true}'

如果这条链路能稳定输出到结束,说明 LB 的超时配置和后端保持机制都到位了。

3.5 域名解析:最关键的一步,别把流量绕回 Cloudflare

LB 创建完成后,控制台会给出一个公网 VIP 和一个对应的完整域名(形如xxx.<region>.oraclecloud.com)。域名解析我推荐直接做一条 CNAME 记录指向这个 LB 域名,而不是指向 IP,这样以后 LB 底层 IP 变化时,你不需要去改 DNS。

很多人在这里会栽一个隐藏的跟头:如果你的域名托管在 Cloudflare,并且 DNS 记录开启了橙色云朵(代理)模式,那么流量依然会先进入 Cloudflare 边缘,再回源到 OCI LB。这样一来,前面那个 100 秒超时限制又阴魂不散地回来了。正确做法是:在 Cloudflare DNS 管理页面,把网关记录的状态改为“仅 DNS”(灰色云朵),让流量直接从客户端DNS解析到OCI公网IP;或者干脆把域名的 DNS 托管迁到 OCI DNS 或其他不支持强制代理的解析服务。

我为什么推荐 CNAME 而不是 A 记录?因为 OCI LB 的 VIP 在极少数运维场景下可能发生变更,CNAME 指向官方域名,解析结果由 OCI 平台维护,切换对使用者完全透明。

4. 迁移过程中遇到的实际问题与排查清单

把域名、端口、超时全部理顺之后,我原以为可以安心收工了,结果还是陆陆续续碰到了一些非常典型的网络问题。这些问题的排查手法,对自建网关的人来说是通用的。

4.1 443 端口连接超时:MTU 和网络策略是两大元凶

现象:客户端执行curl https://ai.example.com,卡了很久之后报“连接超时”或“信号灯超时时间已到”。

这种问题的排查顺序,我建议固定下来:

  1. 先在源站外部机或本机 ping LB 的公网 IP,确认链路基础可达。
  2. 检查安全列表。OCI 默认的安全列表非常严格,如果你创建 VCN 时没有手动添加入站规则,443 端口实际上是被拒绝的。这是新手最容易忽略的点。
  3. 检查健康检查状态。如果后端的健康检查失败,LB 会把流量全部返回 503,而 503 在部分客户端里也会被误报为“连接超时”。
  4. 检查 MTU 一致性。OCI 实例网卡的 MTU 很可能默认设置成 9000 的巨型帧,而客户端的家用路由器和本机网卡几乎都是 1500。当大尺寸数据包(尤其是 TLS 握手阶段的 Certificate 消息)经过链路时,会因为超过中间链路 MTU 被丢弃,表现为连接直接卡死,迟迟响应失败。

MTU 问题有一个非常经典的验证方式:

# 用 ping 探测路径 MTU,-M do 表示禁止分片 ping -M do -s 1472 <目标IP>

如果-s 1472不通,但-s 1000通,就说明链路上存在 MTU 限制。此时需要调整 OCI 实例网卡的 MTU 设置,把mtu从 9000 改回 1500,或者按网络环境适配一个更安全的中间值。容器场景下还要注意 Docker 的mtu参数也要一起改,否则容器内外 MTU 不一致,同样会出现能建连但握手失败的诡异现象。

4.2 Linux 127 秒超时:这不是程序 bug,是 TCP 重传机制

另一个高频现象是:后端服务明明没启动,客户端请求要等整整 127 秒才返回失败。这是因为 Linux 默认的 TCP SYN 重传次数是 6 次,每次超时时间指数递增,重试全部走完之后累计耗时大约 127 秒。

大模型网关如果某个后端实例挂了,而健康检查还没把它标记为 Down,这个 127 秒的等待会让用户觉得服务完全不可用。排查时用ss -t和traceroute确认连接在哪一跳被丢弃。如果希望失败反馈更快一点,可以在服务器上做一个保守调整:

# 降低 SYN 重传次数,让不可达主机的失败反馈从 127 秒缩短到约 7~8 秒 sudo sysctl -w net.ipv4.tcp_syn_retries=3

不过这只是“让报错更快”的治标手段,真正要做的是保证 LB 的健康检查能够及时摘除异常后端,从根上避免请求被发送到一个不存在的服务上。

4.3 流式输出被中途掐断:空闲超时与心跳缺少的典型症状

现象:大模型回答到一半,客户端突然收到流结束或连接重置,但服务端日志显示整个请求是正常处理完的。

这种问题通常不是应用层逻辑错误,而是链路上的某台设备检测到“空闲时间过长”后主动断开了 TCP 连接。大模型在输出前的思考阶段,可能十几秒甚至几十秒都不产生任何 Token;中间如果模型在调用工具,也可能长时间没有数据输出。

我在采用 OCI LB 之后把监听器的空闲超时调到了 300 秒,同时在网关内部实现了 SSE 心跳注释帧,问题彻底消失。这里很想提醒一句:不要只依赖 LB 调参,后端的主动保活同样重要。客户端、LB、互联网中间设备、源站四层链路里,任何一层的“空闲判断”都会导致断流,最稳妥的做法就是让数据流里始终有“呼吸”。

4.4 健康检查的 404 陷阱与域名头透传

还有两个隐蔽问题,非常值得拿出来单独写。

健康检查 404 陷阱:很多健康检查默认探测路径是/,而网关如果不把根路径暴露为 200,而是返回 404,LB 就判断后端不健康。我遇到过运维同事把后端集配好之后,LB 面板上所有后端都红着的场景。解决办法有两个:要么把健康检查路径改成网关明确提供的一个接口,比如/v1/models;要么在健康检查配置里把“期望响应码”改成[200,404],让后端在返回 404 时也被视为存活。按照自己网关的实际情况选一个即可。

X-Forwarded-For 头信任:LB 转发请求时会在 Header 里带上真实的客户端 IP,字段是X-Forwarded-For。网关的日志系统很可能依赖这个字段做来源统计,但一定要让网关只信任来自 LB 的该字段,而不是直接信任外部客户端传入的自定义值,否则任何用户都能伪造来源 IP,日志审计直接失去意义。

我发现不少自建网关翻车,都是因为排障方向从一开始就错了。业务正常时,什么都好;业务一异常,先看是不是域名解析、是不是LB配置、是不是后端服务,而不是一上来就怀疑模型本身,这个顺序能省掉很多无效加班。

5. 一些选型上的建议和最终选择

经历了 Cloudflare 限制和 OCI L7 LB 的迁移之后,我对“大模型网关的网络层选型”有了非常明确的判断标准。

如果你只是自己小范围用,访问量极低,网关超时偶尔失败一次也能接受,那么 Cloudflare Tunnel 依然是最省事的方案:零成本、无需公网 IP、不需要管理证书。而如果你的网关要面向一支开发团队甚至外部客户,提供稳定的 OpenAI 兼容接口,需要跑流式 SSE,那请不要抱着免费 CDN 不放——OCI 的免费 L7 负载均衡器在这套场景下的价值,远不只是“免费”两个字,它把超时的控制权还给了使用者,这是能不能用、敢不敢上生产的分界线。

我目前的架构是:OCI 免费 L7 LB 作为唯一公网入口,443 终结 TLS;后端两台免费的 Arm VM 跑网关,LB 基于 Host 域名做路由,把生产流量、内部监控工具、灰度测试流量分流到不同后端集;DNS 托管在外部正常解析服务,记录直接指向 LB 的 CNAME。整套资源月成本为零,但可以获得可调的超时、实时的健康检查、双可用区的高可用,以及按域名和路径路由的能力。

最后分享一点点体会:网络层的问题,不像代码 bug 那么快地暴露,因为它在大多数时间表现得“还好”,只在流量达到某种特征时才崩溃。大流量、长连接、流式输出,恰好就是把这些问题逼出来的压力测试。遇到超时先别急着调程序的 timeout 参数,把链路里每一层设备的超时策略梳理一遍,比满屏抓日志管用得多。配置完 OCI LB 的那天晚上,我又拿一个超长的测试请求跑了一遍,看着 Token 一个接一个输出到 200 多秒仍未中断,心里那块石头才彻底落了地。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询