☰
Java 程序员第 45 阶段18:网关统一路由大模型接口,配合 Nacos 配置治理,网关高可用:集群部署、负载均衡与容灾方案
2026/9/26 18:18:38 网站建设 项目流程

1. 为什么大模型网关必须做高可用

当公司里所有大模型调用都收敛到一个网关入口后,这个网关就从「一个转发组件」变成了全局单点。它挂掉,对话、Embedding、生成能力全部不可用,影响面比某个业务系统宕机大得多。所以高可用是网关落地的及格线,不是加分项。

我先把目标量化成三个指标,后面所有配置都围绕它们展开。可用性:全年不可用时间控制在 SLO 内,比如 99.95% 对应全年约 4.4 小时。可扩展性:流量翻倍时能水平扩容网关实例扛住。容错性:后端大模型 Provider 抖动、超时、限流时,网关能降级兜底,不雪崩。

整体架构是三层高可用。接入层用 SLB 或 Nginx 对多个网关实例做负载均衡,消除网关单点。网关层是 N 个 Spring Cloud Gateway 无状态实例,全部从 Nacos 拉取路由与配置,彼此对等。Provider 层是大模型服务多实例注册到同一个注册中心,网关通过lb://做客户端负载均衡。

这套架构里 Nacos 既是配置中心,下发路由、缓存开关、限流规则,也是注册中心做服务发现。所有高可用策略都通过 Nacos 集中管理、动态生效,和配置治理主线一脉相承。下面从 TaoToken 的接入准备开始,一步步把可复制的配置搭起来。

2. TaoToken 前置准备:统一 Key 与 API 通道

网关要统一路由大模型接口,前提是有一个稳定的上游通道。TaoToken 在这里承担的角色是统一 Key 管理和 API 通道,网关只需要面向一个兼容 OpenAI 的接口规范,不用为每个厂商单独写适配。你可以先到官网了解整体能力,再进控制台创建 Key。

具体动作是这样:打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 了解接入方式,然后进入控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 创建 API Key。Key 创建后只显示一次,建议直接写进 Nacos 配置,不要硬编码在代码里。API 基地址用 https://taotoken.net/api,注意这个地址不加 UTM 参数,保持干净。

这里有个容易踩的坑:很多人把 Key 直接写在application.yml里提交到 Git,后面轮换 Key 时要改代码重新发版。正确做法是把 Key 作为 Nacos 配置项,网关启动时拉取,轮换时只改 Nacos 不动代码。配置骨架大概长这样,api-key和base-url都从 Nacos 注入:

llm: gateway: base-url: https://taotoken.net/api api-key: ${LLM_API_KEY:sk-xxxx} connect-timeout: 3000 read-timeout: 60000

如果你还在选模型阶段,可以先用模型对话页面验证 Key 是否可用,确认通道通了再往网关里接。地址是 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。长期做编码和 Agent 场景的话,Coding Plan 会更省心,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。Key 和接入文档在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 与 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。

3. 可复制配置:网关集群 + Nacos 治理

3.1 无状态是横向扩容的前提

网关本身不保存会话状态,缓存是本地的、可丢失,连接是短连接或中连接,所以横向扩容毫无障碍。部署时每个网关实例配置相同的 Nacos 地址、相同的路由 DATA_ID,启动后从 Nacos 拉取全量路由即可对外服务。下面这份配置可以直接复制,把NACOS_ADDR换成你的实际地址:

spring: application: name: llm-gateway cloud: nacos: discovery: server-addr: ${NACOS_ADDR:127.0.0.1:8848} namespace: llm-prod group: LLM_GATEWAY config: server-addr: ${NACOS_ADDR:127.0.0.1:8848} namespace: llm-prod group: LLM_GATEWAY file-extension: yaml gateway: discovery: locator: enabled: true

discovery.locator.enabled开启基于服务发现的路由,这样 Provider 实例上下线时网关能自动感知。路由规则本身也放 Nacos,DATA_ID 用llm-gateway-routes.yaml,内容示例:

spring: cloud: gateway: routes: - id: llm-chat uri: lb://llm-provider predicates: - Path=/api/llm/chat/** filters: - StripPrefix=2 - id: llm-embed uri: lb://llm-provider predicates: - Path=/api/llm/embed/** filters: - StripPrefix=2

3.2 接入层负载均衡与优雅上下线

多个网关实例前面挂一个 SLB 或 Nginx,用轮询或最小连接把外部流量分摊到各网关。注意网关前面的反代要开启健康探测,自动摘除不健康实例。对 SSE 流式路径,Nginx 务必proxy_buffering off,否则流式响应会被缓冲住,前端迟迟收不到内容。

upstream llm_gateway { least_conn; server 10.0.1.11:8080 max_fails=3 fail_timeout=15s; server 10.0.1.12:8080 max_fails=3 fail_timeout=15s; server 10.0.1.13:8080 max_fails=3 fail_timeout=15s; keepalive 64; } server { location /api/llm/ { proxy_pass http://llm_gateway; proxy_buffering off; proxy_read_timeout 120s; health_check uri=/actuator/health interval=5s; } }

扩缩容时务必优雅。实例收到SIGTERM后,先从 Nacos 和 SLB 摘流量,等待在途请求处理完再退出。Spring Boot 的server.shutdown: graceful配合spring.lifecycle.timeout-per-shutdown-phase就能实现:

server: shutdown: graceful spring: lifecycle: timeout-per-shutdown-phase: 30s

3.3 双层负载均衡策略

高可用里有两层负载均衡,含义不同,容易混淆。外层 LB 是 SLB 或 Nginx 把流量分到多个网关实例。内层 LB 是网关把请求分到多个大模型 Provider 实例,由 Gateway 的ReactiveLoadBalancerClientFilter基于服务发现完成,也就是lb://service-name。

内层策略默认是轮询。但大模型场景下不同 Provider 实例算力可能不均,有的卡多有的卡少,轮询会造成快的被拖慢、慢的被压垮。更优的是加权响应时间或一致性哈希。下面这张表帮你按场景选:

策略优点缺点大模型场景适配
RoundRobin 轮询简单、均匀无视实例能力差异实例同构时可用
Random 随机无状态波动大不推荐
WeightedResponseTime慢实例少分流量需统计响应时间异构 GPU 集群推荐
一致性哈希同 key 落同实例,利于本地缓存实例变动时重分布带本地缓存时推荐
最少连接偏好空闲实例需维护连接数流式长连接推荐

Nacos 注册中心支持给每个实例设置weight,Gateway 的负载均衡会按权重分配。你可以在 Nacos 控制台把新版本 Provider 实例权重调小做灰度,调大做放量,流量配比由 Nacos 集中掌控。自定义选择器的骨架如下,核心是取请求里的 tenant 或 model 作为哈希键,再按权重选择:

public class LlmLoadBalancer implements ReactorServiceInstanceLoadBalancer { @Override public Mono<Response<ServiceInstance>> choose(Request request) { // 1. 取请求中的 tenant / model 作为哈希键 // 2. 按 Nacos 实例 weight 做加权随机选择 // 3. 返回选中的 ServiceInstance,交给 Gateway 转发 } }

4. 验证请求:故障转移与配置热更新

配置写完必须验证,否则高可用只是纸面文章。我分两个动作来测:故障转移和配置热更新。

故障转移验证:启动两个网关实例,注册到同一个 Nacos。用curl连续打 20 次请求,观察是否均匀落到两个实例。然后手动停掉其中一个实例,再打 20 次,确认请求全部落到存活实例,且没有报错。命令如下:

for i in $(seq 1 20); do curl -s -o /dev/null -w "%{http_code} " \ -X POST http://10.0.1.11:8080/api/llm/chat \ -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hi"}]}' done echo

预期结果是 20 个 200,停掉一个实例后仍然全是 200,说明接入层健康探测和网关无状态生效了。如果出现 502 或 503,检查 Nginx 的max_fails和fail_timeout是否配置,以及/actuator/health是否可访问。

配置热更新验证:在 Nacos 控制台修改llm-gateway-routes.yaml,比如给llm-chat路由加一个AddResponseHeader过滤器,保存后不重启网关,直接再打一次请求,看响应头里是否出现新加的字段。如果出现了,说明 Nacos 配置监听生效。这一步很关键,它决定了你后面调限流阈值、切 Provider 权重时能不能不重启。

curl -s -D - -o /dev/null \ -X POST http://10.0.1.11:8080/api/llm/chat \ -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hi"}]}' \ | grep -i "x-gateway"

5. 本篇常见错排查

5.1 网关启动报 Nacos 连接失败

最常见的是server-addr写错或 namespace 不匹配。Nacos 的 namespace 用的是命名空间 ID,不是名称,控制台里复制 ID 再填。另外 group 要一致,配置和发现用同一个 group,否则拉不到配置。如果本地开发连远程 Nacos,确认网络可达,别用127.0.0.1却期望连到服务器。

5.2 路由不生效,请求 404

先看spring.cloud.gateway.discovery.locator.enabled是否为 true,再看 Nacos 里的路由 DATA_ID 是否和file-extension对应。比如file-extension: yaml,DATA_ID 就得是xxx.yaml。还有StripPrefix的位数要对,/api/llm/chat/**去掉两层前缀后才是 Provider 的真实路径,位数错了就会 404。

5.3 流式响应被缓冲,前端收不到

这是 Nginx 层的问题,不是网关。确认proxy_buffering off加在了 SSE 路径的 location 里。另外proxy_read_timeout要够大,大模型生成慢,默认 60s 可能不够,调到 120s 或更长。如果用了 SLB,也要确认 SLB 侧没开响应缓冲。

5.4 熔断降级不触发

检查 Resilience4j 或 Sentinel 的规则是否真的下发到了网关。Sentinel 网关流控规则通过 Nacos 数据源下发时,rule-type要写gw-flow,写错成flow规则不会生效。另外降级过滤器的@Order要足够小,保证它在路由转发之前执行,否则异常已经被下游处理掉了。

5.5 优雅停机杀掉在途请求

server.shutdown: graceful只对 Spring Boot 内嵌容器生效,如果前面有 Nginx,还要确保 Nginx 在实例摘流后不再转发新请求。顺序是:先调 Nacos 注销接口摘流量,等timeout-per-shutdown-phase时间,再发SIGTERM。顺序反了,在途请求会被直接切断。

6. 限流保护与容灾兜底

高可用不仅要防后端挂,还要防自己被冲垮和把后端冲垮。网关是限流的最佳位置,在流量入口统一做流控,保护整条链路。Spring Cloud Gateway 集成 Sentinel 后,可以基于路由 ID、API 分组、来源应用、参数做限流,支持快速失败、排队等待、预热三种效果。

spring: cloud: sentinel: transport: dashboard: ${SENTINEL_ADDR:127.0.0.1:8858} datasource: gw-flow: nacos: server-addr: ${NACOS_ADDR:127.0.0.1:8848} namespace: llm-prod groupId: LLM_GATEWAY dataId: sentinel-gateway-flow-rules rule-type: gw-flow

Nacos 里的规则示例,按路由限流,对话每秒 100 次快速失败,Embedding 每秒 500 次排队等待:

[ { "resource": "llm-chat", "count": 100, "intervalSec": 1, "controlBehavior": 0, "burst": 20 }, { "resource": "llm-embed", "count": 500, "intervalSec": 1, "controlBehavior": 2, "maxQueueingTimeoutMs": 500 } ]

controlBehavior里 0 是快速失败,1 是预热,2 是排队等待。对话用快速失败加友好报错,Embedding 这种可短暂排队的用排队等待。更精细的还可以按model参数做热点限流,某模型被刷爆时只拦该模型,不影响其他模型。

熔断降级方面,用 Resilience4j 或 Sentinel 的熔断规则,当对某个 Provider 的错误率或慢调用比例超过阈值,自动跳闸,后续请求在熔断窗口内直接走降级逻辑,给 Provider 喘息时间。降级不是返回错误,而是返回有业务意义的兜底,比如返回缓存中的上一次结果,或切换到备用 Provider,或返回结构化的「模型繁忙」报文。

@Component @Order(-3) public class LlmFallbackGlobalFilter implements GlobalFilter { private final Cache<String, String> fallbackCache; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return chain.filter(exchange) .onErrorResume(TimeoutException.class, e -> fallback(exchange, "模型响应超时")) .onErrorResume(CallNotPermittedException.class, e -> fallback(exchange, "熔断中,已降级")) .onErrorResume(ResponseStatusException.class, e -> fallback(exchange, "后端异常")); } private Mono<Void> fallback(ServerWebExchange exchange, String reason) { String key = exchange.getAttribute("cacheKey"); String cached = fallbackCache.getIfPresent(key); String body = (cached != null) ? cached : "{\"error\":\"model_unavailable\",\"reason\":\"" + reason + "\"}"; byte[] bytes = body.getBytes(StandardCharsets.UTF_8); exchange.getResponse().getHeaders().add("X-Fallback", "true"); DataBuffer buf = exchange.getResponse().bufferFactory().wrap(bytes); return exchange.getResponse().writeWith(Mono.just(buf)); } }

多机房容灾的话,网关集群与 Provider 集群在两个机房各部署一套,Nacos 做跨机房同步,接入层 SLB 配置主备机房健康探测,主机房不可用时流量切到备机房。按业务重要性选容灾等级:测试用单实例,内部工具用双实例加 SLB,生产默认多实例加熔断限流,核心业务上多机房双活。

落地清单我整理成几条:网关无状态化,所有策略走 Nacos,随时扩缩容;接入层健康探测探/actuator/health,自动摘流;优雅上下线用 graceful shutdown 加 Nacos 注销;双层负载均衡,外层 SLB 分到网关,内层lb://加权或哈希分到 Provider;熔断加降级兜底,错误率超阈值跳闸,降级返回缓存或友好报文;入口限流用 Sentinel 网关流控经 Nacos 下发;核心业务上多机房预案。

到这里,网关在统一路由加配置治理的基础上,又补齐了高可用和容灾这层护甲。下一步可以把治理做得更严,也就是谁能动配置、动了什么、能否审计。如果你还没把 Key 和通道准备好,先去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 创建,接入细节看 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,长期编码场景直接上 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。

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

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

立即咨询