Java 程序员第 45 阶段07:网关统一路由大模型接口,配合 Nacos 配置治理,服务发现联动:Nacos 注册中心 + Gateway 路由自动同步微服务实例

📅 2026/7/24 20:28:39
Java 程序员第 45 阶段07:网关统一路由大模型接口,配合 Nacos 配置治理,服务发现联动:Nacos 注册中心 + Gateway 路由自动同步微服务实例
第 06 篇我们解决了路由规则能热更新。但还有一个更底层的问题没解决路由里的 uri 写的是 lb://llm-openai-proxy这个 llm-openai-proxy 到底对应哪几台机器、lb:// 是怎么把请求打到具体实例的如果某个大模型代理服务扩容了一台、或者某台宕机下线了网关怎么知道答案就是**服务发现Service Discovery**。Nacos 同时是配置中心和注册中心——本篇我们把后一个身份用起来让网关通过 Nacos 注册中心自动感知每个大模型微服务的所有健康实例并实时同步到负载均衡路由里。这样加机器不用改配置、宕机自动摘流量就成了天然能力。为什么需要服务发现联动Nacos 注册中心与 DiscoveryClient 原理Gateway 如何把 lb:// 解析为实例列表实战开启服务发现路由自动同步大模型微服务实例的动态上下线实战自定义负载均衡与实例过滤最佳实践与踩坑点本篇小结1. 为什么需要服务发现联动回到一个朴素的问题没有服务发现时路由 uri 只能写死 IP。# 写死 IP 的痛扩缩容、故障都要人肉改uri: http://10.20.30.11:8080写死 IP 有三个致命伤对大模型服务尤其明显**弹性伸缩失效**大模型推理服务常根据 GPU 利用率扩缩容如 vLLM 节点从 2 台扩到 6 台写死 IP 意味着新节点永远接不到流量。**故障要人工摘除**某台推理机显存爆了、进程假死写死 IP 的网关仍会把请求转过去得到一堆 500。**多环境难以复用**测试环境一套 IP、生产环境另一套 IP网关配置无法统一。服务发现的本质是**把服务名 - 实例列表的映射交给一个中心Nacos维护网关只认服务名实例的增删由注册中心自动广播**。结合第 06 篇的动态路由uri: lb://llm-openai-proxy 就能在运行时被解析成当前在 Nacos 上健康的全部 llm-openai-proxy 实例并且**实例变化自动同步无需任何人工介入**。2. Nacos 注册中心与 DiscoveryClient 原理Nacos 注册中心维护一张服务名 - 实例的注册表。每个微服务实例启动时向 Nacos 注册自己IP、端口、健康状态、元数据、权重、集群名并定时发心跳Nacos 据此剔除长时间失心跳的实例。Spring Cloud 用统一抽象 DiscoveryClient 屏蔽不同注册中心差异public interface DiscoveryClient {String description();ListServiceInstance getInstances(String serviceId); // 取某服务的所有实例ListString getServices(); // 取所有服务名}Nacos 的实现是 NacosDiscoveryClient。Gateway 的 DiscoveryClientRouteDefinitionLocator 正是依赖它**为注册表中的每一个服务自动生成一条路由**前提是你开启了 spring.cloud.gateway.discovery.locator.enabledtrue。角色职责------微服务实例启动时注册、定时心跳、下线时注销Nacos Server维护注册表、健康检查、变更推送DiscoveryClient网关侧读取注册表的统一接口DiscoveryClientRouteDefinitionLocator把每个服务变成一条 Gateway 路由关键点**注册表的变更实例上下线会触发 DiscoveryClient 侧缓存更新进而让 DiscoveryClientRouteDefinitionLocator 重新产出 RouteDefinition**。但这仍要走第 06 篇讲的 RefreshRoutesEvent 才能最终反映到 Route 缓存。Spring Cloud Alibaba 已帮你接好这条链路但理解它对排障至关重要。3. Gateway 如何把 lb:// 解析为实例列表lb:// 是 load balance 的协议前缀。当 Gateway 遇到 uri: lb://llm-openai-proxy它会交给 ReactiveLoadBalancerClientFilter 处理从 uri 抽出服务名 llm-openai-proxy通过 ReactiveLoadBalancerFactory 拿到该服务的 ReactiveLoadBalancer负载均衡器内部调用 ServiceInstanceListSupplier——它的默认实现会去问 DiscoveryClient 要实时实例列表按负载均衡策略默认 RoundRobin可换 Random/Nacos 权重选一个实例把 lb://llm-openai-proxy 重写成 http://选定的IP:端口继续后续过滤器链。// lb:// 解析核心示意框架内部仅帮助理解String serviceId llm-openai-proxy;ServiceInstance instance loadBalancer.choose(serviceId); // 从 DiscoveryClient 拿到的实例里挑一个URI realUri rebuildUri(uri, instance); // http://10.20.30.11:8080对大模型服务来说这步还隐含一个价值**请求被均衡到多台推理机单台显存/算力瓶颈被分摊**。当某台推理机挂了DiscoveryClient 拿到的实例列表里就没有它负载均衡器自然不会选它——这就是自动摘流。4. 实战开启服务发现路由自动同步要在网关侧启用服务发现自动生成路由配置非常轻量。**pom.xml 依赖**网关侧dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-gateway/artifactId/dependency**application.yml 关键配置**spring:cloud:nacos:discovery:server-addr: 127.0.0.1:8848namespace: llm-prodgroup: LLM_GROUPgateway:discovery:locator:enabled: true # 开启服务发现路由自动生成lower-case-service-id: true # 服务名转小写避免路径大小写问题routes:# 你也可以保留自定义路由与服务发现路由共存- id: llm-openaiuri: lb://llm-openai-proxypredicates:- Path/v1/llm/openai/**filters:- StripPrefix2开启 discovery.locator.enabledtrue 后Nacos 里只要有一个服务叫 llm-openai-proxy网关就自动多出一条 /llm-openai-proxy/** 的路由。但这条自动路由路径前缀是服务名不够优雅**生产上我们更推荐第 06 篇的自定义 Nacos 路由 本篇的 lb:// 解析组合**路由路径自己定义实例解析交给服务发现。微服务侧的注册大模型代理服务spring:application:name: llm-openai-proxycloud:nacos:discovery:server-addr: 127.0.0.1:8848namespace: llm-prodmetadata:supplier: openaiversion: v15. 大模型微服务实例的动态上下线这是本篇最精彩的地方——**让大模型推理节点像云资源一样弹性**。假设我们用 llm-vllm 跑本地大模型节点数随 GPU 负载在 2~6 之间波动。当新节点启动它执行// 注册逻辑由 spring-cloud-starter-alibaba-nacos-discovery 自动完成// 你只需保证应用名与 metadata 正确无需手写注册代码SpringBootApplicationEnableDiscoveryClient // 显式声明新版本可省略自动装配public class VllmProxyApplication {public static void main(String[] args) {SpringApplication.run(VllmProxyApplication.class, args);}}注册成功后Nacos 推送变更网关 DiscoveryClient 缓存更新DiscoveryClientRouteDefinitionLocator 重新产出定义链路末尾的 RefreshRoutesEvent 让 Route 缓存刷新。此后新请求即可被负载均衡到这台新节点。当节点优雅下线收到 SIGTERMSpring 的 DisposableBean/PreDestroy 会触发反注册实例从 Nacos 移除流量自动不再路由过去——**典型场景下用户零感知**。扩缩容时序以扩容为例T0 新 vLLM 节点启动向 Nacos 注册 10.20.30.16:8000T1 Nacos 推送服务列表变更给网关T2 网关 DiscoveryClient 缓存更新为 3 个实例T3 RefreshRoutesEvent 重建 RouteT4 新请求按负载均衡落到 10.20.30.16开始分担流量场景写死 IP服务发现联动---------扩容一台改配置 重启网关自动感知秒级接流宕机一台人工摘除否则持续 500心跳超时自动剔除灰度一台改路由 重启打 metadata 版本按元数据结构路由6. 实战自定义负载均衡与实例过滤默认轮询RoundRobin对大模型推理并不友好——不同 GPU 机型算力不同应该按**权重**或服务**元数据**选实例。Nacos 原生支持权重我们接上 NacosLoadBalancer。Configurationpublic class LlmLoadBalancerConfig {// 使用 Nacos 权重负载均衡依赖 nacos 权重字段Beanpublic ReactorLoadBalancerServiceInstance nacosLoadBalancer(Environment environment,LoadBalancerClientFactory factory) {String name environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new NacosLoadBalancer(factory.getLazyProvider(name, ServiceInstanceListSupplier.class),name);}}更进一步只把请求转发到健康的、且版本匹配的实例。自定义 ServiceInstanceListSupplierpublic class TaggedInstanceSupplier implements ServiceInstanceListSupplier {private final ServiceInstanceListSupplier delegate;public TaggedInstanceSupplier(ServiceInstanceListSupplier delegate) {this.delegate delegate;}Overridepublic String getServiceId() { return delegate.getServiceId(); }Overridepublic FluxListServiceInstance get() {return delegate.get().map(instances -instances.stream().filter(i - openai.equals(i.getMetadata().get(supplier))).filter(i - v1.equals(i.getMetadata().get(version))).collect(Collectors.toList()));}}这样网关层就能基于实例元数据做**同版本路由、按供应商隔离**对多供应商大模型场景极为实用一套 llm-openai-proxy 服务下可以混部不同版本的代理网关只挑符合 metadata 的实例。7. 最佳实践与踩坑点服务发现联动看似开个开关就行但大模型场景有几个专属坑踩坑点现象正确做法---------心跳间隔太长宕机后几十秒仍有流量打过去适当缩短 spring.cloud.nacos.discovery.heart-beat-interval如 1~3s配合存活探针只开 locator 不自定义路由路径暴露服务名 /llm-openai-proxy/**不雅且易泄露内部结构关掉自动 locator用第06篇自定义 Nacos 路由 lb:// 解析权重未配置强弱机型平分流量弱机成瓶颈Nacos 控制台配置实例权重用 NacosLoadBalancer元数据缺失无法按供应商/版本隔离注册时强制填 supplier、version 等 metadata实例列表缓存未刷新扩容后新节点迟迟不接流确认 DiscoveryClient 缓存 TTL 合理必要时手动 RefreshRoutesEvent跨 namespace 不通网关找不到服务网关与微服务必须同 namespace 同 group额外建议**给推理节点配就绪探针readiness**只有模型真正加载完、能出 token 了才允许注册避免进程在但模型没好时被打挂。**保护注册中心**网关对 Nacos 是只读订阅不要为图省事给网关写权限注册中心的可用区AZ要和高可用部署对齐。**结合第 06 篇做治理**用 Nacos 配置管理是否启用某服务的自动路由实现服务发现路由的开关化治理。8. 本篇小结本篇我们把网关统一路由大模型接口 Nacos 配置治理推进到第二环——**服务发现联动**Nacos 既是配置中心也是注册中心微服务实例注册、心跳、下线Nacos 维护实时注册表网关通过 DiscoveryClient 读取注册表lb://服务名 在运行时被解析为当前健康实例列表并做负载均衡实例上下线经 Nacos 推送 → DiscoveryClient 缓存更新 → 路由定义刷新 → Route 缓存重建全程自动用权重负载均衡 元数据过滤可让大模型流量按机型算力、供应商版本精确调度。至此网关已经能做到路由规则动态、后端实例动态的双动态。下一篇第 08 篇我们给这套动态网关穿上安全服——**网关层统一鉴权JWT/OAuth2 Token 校验与下游转发实战**确保只有合法调用方能访问大模型接口。