Spring Cloud Gateway整合LoadBalancer:从加权策略到超时熔断实践
Spring Cloud Gateway 配 Spring Cloud LoadBalancer很多人以为就是路由配置里写一行lb://的事但真正在生产环境把它跑稳远没有想象中那么简单。我这篇要说的就是自己从零把 Gateway 和 LoadBalancer 整合起来、再一步步做到自定义加权、超时重试和熔断的完整过程。适合刚把微服务跑起来、又被网关转发搞到头大的同学也适合那些“路由能通但说不清楚流量到底怎么选实例”的进阶读者。我在写这篇之前其实是带着几个问题去翻源码的lb://协议到底是被谁解析掉的自定义负载均衡策略应该从哪个接口下手Nacos 里配好的权重为什么在网关里不生效如果你也有类似的疑问那这篇文章应该正好对胃口。1. 基础整合让第一个 lb:// 路由真正跑起来1.1 网关里为什么必须有一个客户端负载均衡器先想清楚一个问题网关本身就是一台反向代理为什么它还需要“客户端负载均衡”网关背后的服务在注册中心里往往不止一个实例。比如order-service可能有三台机器网关接收到/api/order/**的请求后必须从这三台里挑一台把请求转发过去。谁来挑两种思路服务端负载均衡像 Nginx 那样所有请求先进一个中心节点由中心节点统一分发。网关如果把负载均衡逻辑交给外部代理等于所有流量都要再绕一道而且中心节点一挂整个入口就瘫痪。客户端负载均衡网关自己维护一份服务实例列表每次转发前用本地算法选一个实例直接打过去。没有中间代理链路更短可用性也更高。Spring Cloud Gateway 选的是第二种。它从注册中心Nacos、Eureka、Consul拿到服务实例列表后交给 LoadBalancer 做选择。这就是为什么 Gateway 必须和 LoadBalancer 深度整合——路由只管“转发到哪个服务”LoadBalancer 才决定“转发到哪台机器”。1.2 依赖选择少加一个就可能 503Spring Cloud 2020.0.x 版本之前网关的客户端负载均衡默认由 Ribbon 提供。2020.0 之后 Ribbon 退役spring-cloud-starter-loadbalancer成了官方默认方案。很多老项目升级后忘了换依赖网关日志就会蹦出一堆No instances available。我见过好几个人踩这个坑pom.xml里只加了spring-cloud-starter-gateway和注册中心依赖没加 loadbalancer结果路由一访问就 503。Gateway 本身不负责实例选择它只负责把请求转发出去真正查实例列表、挑实例的是 LoadBalancer这个依赖漏掉lb://协议根本没法解析。一份能正常工作的最小依赖清单大致是这个样子dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId /dependency dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-loadbalancer/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency如果你的服务用 Eureka把 Nacos 依赖换成对应的 Eureka starter 就行LoadBalancer 部分不用变。这里特别提醒一句Gateway 自己的 starter 里已经带了 WebFlux所以绝对不能再加spring-boot-starter-web否则启动直接报Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway。这个问题太典型了后面避坑章节我会再展开。1.3 路由配置与负载均衡生效验证依赖配好之后路由配置文件里最核心的一行就是uri: lb://order-service。server: port: 8080 spring: application: name: gateway-server cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: routes: - id: order-service uri: lb://order-service predicates: - Path/api/order/** filters: - StripPrefix1这里的lb://不是随便写的协议名它相当于给网关一个标记这个路由的流量需要走 LoadBalancer 挑实例。StripPrefix1表示去掉第一段路径/api所以请求/api/order/list转发到后端时变成/order/list。光配完还不能算完得真实验证负载均衡在起作用。我的做法是起三个order-service实例分别在日志里打印自己的端口或机器名然后从网关连打几十次请求看三个实例的日志计数是否大致平均。如果所有流量都打在同一个实例上说明 LoadBalancer 根本没参与问题多半出在依赖缺失或服务名对不上。实际测的时候我习惯加一个临时接口比如/metrics/local返回当前实例 IP 和端口RestController public class LocalInfoController { Value(${spring.cloud.nacos.discovery.ip:unknown}) private String ip; Value(${server.port}) private String port; GetMapping(/local) public String local() { return instance: ip : port; } }然后循环访问http://网关地址/api/order/local看返回结果是否在多个实例间切换。能切换说明整合成功。2. 关键链路拆解lb:// 是如何被翻译成真实实例地址的基础跑通之后我强烈建议你把原理想明白。很多人只会配lb://出了问题只能靠瞎试就是因为不知道这条链路上每一步是谁在干活。2.1 从 URL 到请求上下文RouteToRequestUrlFilter 在做什么网关收到一个请求比如GET /api/order/list先由路由谓词匹配到order-service这条路由。匹配成功后Gateway 进入全局过滤器链第一个关键角色是RouteToRequestUrlFilter。这个过滤器的职责是把原始请求的 URL 换掉。它发现当前路由的uri是lb://order-service于是把请求的目标地址改写成lb://order-service/api/order/list。注意此时 URL 里还是lbscheme根本没有真实 IP所以这个 URL 还不能用来发网络请求只能算一个“半成品”。RouteToRequestUrlFilter的 order 在源码里是ROUTE_TO_URL_FILTER_ORDER值为 10000。它必须在路由匹配之后、真正发请求之前执行顺序排得很靠前。2.2 实例选择的实际执行者ReactiveLoadBalancerClientFilterURL 被改写成lb://order-service/api/order/list之后轮到ReactiveLoadBalancerClientFilter出场。这个过滤器的 order 是 10150比RouteToRequestUrlFilter稍微靠后。它做的事情很直接检查当前请求的 URL scheme 是不是lb。是的话从 URL 里取出服务名order-service。调用ReactiveLoadBalancer默认是RoundRobinLoadBalancer选择一台真实实例。把 URL 的 host 和 port 替换成选中实例的 IP 和端口重新构造请求。也就是说真正干活的 LoadBalancer 被这个 filter 包在中间。你自定义的负载均衡策略最终也是在这个环节被调用。这里有个细节选完实例后ReactiveLoadBalancerClientFilter会把服务实例信息放进请求属性里后续的NettyRoutingFilter拿这个信息发起真实 HTTP 调用。如果你写自定义过滤器想获取最终转发到了哪个实例可以从ServerWebExchange的属性里取键名是ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER附近的常量。当然不同版本键名略有差异建议直接断点看源码。2.3 默认轮询是怎么被调起来的Spring Cloud LoadBalancer 默认的策略是轮询RoundRobin由RoundRobinLoadBalancer实现。它的核心逻辑是拿到实例列表后用一个AtomicInteger做自增计数对列表长度取模得到本次选中的实例下标。这个实现很简单但也意味着两点它完全不管每个实例的权重哪怕某台机器配置高、能扛三倍流量轮询模式下它也和其他机器一样只接一份流量它不管实例当前健康状态实例列表来自注册中心如果某个实例注册了但实际已经假死轮询依然可能选中它。所以生产环境如果机器规格不一致或者要做金丝雀发布、权重引流默认策略基本不够用。这也是后面要自定义策略的根本原因。2.4 35 秒缓存LoadBalancer 的实例列表更新时间还有一个非常容易被忽略的机制实例列表缓存。Spring Cloud LoadBalancer 默认会缓存一份服务实例列表缓存 TTL 默认 35 秒spring.cloud.loadbalancer.cache.ttl。这个设计本身是好的能避免每次请求都去注册中心拉列表降低注册中心压力。但副作用是实例上下线、权重调整不会立刻反映到网关的转发行为上。你刚在 Nacos 里把某个实例下线网关可能还要最多 35 秒才会把它踢出本地缓存。调试的时候可以直接把缓存关掉spring: cloud: loadbalancer: cache: enabled: false但生产环境不建议关正常业务对 35 秒的感知延迟是可以接受的。如果你希望权重调整快速生效可以把 TTL 调小比如 5 秒代价是注册中心查询频率变高。我的建议是生产先保持默认只有做细致的发布策略时才需要调。3. 进阶玩法按权重自定义负载均衡策略实操心得默认策略可以应付 80% 的场景但一旦你开始做多规格实例混部、按机器能力分流、灰度验证自定义负载均衡策略就躲不开了。这一节的代码可以直接抄。3.1 认识三个核心扩展接口自定义负载均衡策略最核心的接口有三个ServiceInstanceListSupplier负责提供某个服务的实例列表可以从注册中心拉也可以本地写死甚至可以包装一层做过滤。ReactorLoadBalancerServiceInstance负载均衡策略接口choose(Request request)方法返回MonoResponseServiceInstance。Request负载均衡请求上下文里面带着服务名serviceId和可选的hint策略可以根据 hint 做更细的分流。自定义策略时你实际需要做的事就两类要么自己实现ReactorLoadBalancer替换默认的选实例逻辑要么自定义ServiceInstanceListSupplier去过滤或加工实例列表。多数场景下实现一个自定义的ReactorLoadBalancer就够了。3.2 Nacos 权重为什么默认不生效Nacos 控制台支持给每个实例设置权重默认权重是 1取值范围 0 到 100。这个权重在 Nacos 自身的客户端负载均衡逻辑里是生效的但 Spring Cloud LoadBalancer 完全不知道。原因在于Spring Cloud 的ServiceInstance接口只定义了getInstanceId()、getServiceId()、getHost()、getPort()、getMetadata()这些标准方法没有 weight 字段。Nacos 的NacosServiceInstance虽然内部持有权重信息但 LoadBalancer 拿到的只是一个标准化的ServiceInstance默认的RoundRobinLoadBalancer更是连看都不会看权重一眼。所以你想让 Nacos 权重在网关里生效只能自己在策略里把权重解析出来。不同版本的 Nacos 客户端权重塞在 metadata 里的 key 可能不一样。我自己在 Nacos Client 2.2.x 的环境里测试metadata 里能拿到nacos.weight稳妥起见代码里要做好兜底读不到就按权重 1 处理。3.3 手写一个加权随机负载均衡器加权随机是最常见的方案权重越高被选中的概率越大。核心算法很简单就是把所有实例的权重加起来算总量然后生成一个 0 到总量的随机数随机数落在哪个实例的权重区间就选哪个实例。完整实现如下import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.DefaultRequest; import org.springframework.cloud.client.loadbalancer.Request; import org.springframework.cloud.client.loadbalancer.Response; import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancer; import org.springframework.cloud.client.loadbalancer.reactive.ReactorServiceInstanceLoadBalancer; import reactor.core.publisher.Mono; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; public class NacosWeightedLoadBalancer implements ReactorServiceInstanceLoadBalancer { private final String serviceId; private final ServiceInstanceListSupplier supplier; public NacosWeightedLoadBalancer(String serviceId, ServiceInstanceListSupplier supplier) { this.serviceId serviceId; this.supplier supplier; } Override public MonoResponseServiceInstance choose(Request request) { // 拉取服务实例列表交给加权选择逻辑 return supplier.get() .next() .map(this::doChoose) .defaultIfEmpty(Response.empty()); } private ResponseServiceInstance doChoose(ListServiceInstance instances) { if (instances null || instances.isEmpty()) { return Response.empty(); } double totalWeight instances.stream() .mapToDouble(this::extractWeight) .sum(); // 随机落点决定选中哪个实例 double offset ThreadLocalRandom.current().nextDouble(totalWeight); for (ServiceInstance instance : instances) { double weight extractWeight(instance); if (offset weight) { return Response.withContext(instance, instances); } offset - weight; } return Response.withContext(instances.get(instances.size() - 1), instances); } private double extractWeight(ServiceInstance instance) { MapString, String metadata instance.getMetadata(); String weightStr metadata.get(nacos.weight); if (weightStr null) { weightStr metadata.get(weight); } if (weightStr ! null) { try { return Double.parseDouble(weightStr); } catch (NumberFormatException ignored) { // 解析失败按默认权重 1 处理 } } return 1.0D; } }这里我直接实现了ReactorServiceInstanceLoadBalancer它是ReactorLoadBalancer的一个子接口方法签名更明确也是官方推荐的方式。supplier从哪来一般情况下可以用DiscoveryClientServiceInstanceListSupplier它内部会通过DiscoveryClient从注册中心拉实例。如果你发现 metadata 里确实没有权重信息就需要换一条路自定义ServiceInstanceListSupplier直接拿到 Nacos 原生的com.alibaba.nacos.api.naming.pojo.Instance对象从getWeight()方法里取权重。这个方案更彻底但代码也要多写一层包装。我建议先打印 metadata 看实际情况再决定用哪种方案别一上来就搞复杂版本。3.4 把自定义策略绑定到指定服务光有负载均衡器类还不够你得把它注册成 Spring Bean并且告诉 LoadBalancer这个策略是给哪个服务用的。官方推荐的做法是LoadBalancerClient注解。配置类写成这样LoadBalancerClient( name order-service, configuration OrderServiceLoadBalancerConfig.class ) public class OrderServiceLoadBalancerConfig { Bean public ReactorLoadBalancerServiceInstance weightedLoadBalancer( ObjectProviderServiceInstanceListSupplier supplierProvider, Environment environment) { String serviceId environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); ServiceInstanceListSupplier supplier new DiscoveryClientServiceInstanceListSupplier(serviceId); return new NacosWeightedLoadBalancer(serviceId, supplier); } }这里有个大坑必须强调这个配置类一定不要放在主启动类的ComponentScan扫描包下。Spring Cloud LoadBalancer 会为每个服务创建一个独立的LoadBalancerClientFactory如果你的配置类带上了Configuration且又在扫描范围内它会被当成全局配置所有服务都会走这个自定义负载均衡器。一旦order-service的策略应用到别的服务上很可能因为 metadata 里没有权重 key全部退化成默认权重反而把原有策略搞乱。正确定位配置类单独放一个子包要么不加Configuration要么确保它不在主扫描路径下。如果要全局生效可以不用注解直接在 LoadBalancer 的自动配置类里替换默认 Bean但生产环境我建议先服务级别控制跑稳了再说全局。3.5 进一步按请求头标记做灰度分流加权随机只解决“按能力分流”的问题灰度场景通常还要求“按请求特征分流”。比如带version: gray的请求只打到灰度实例其他请求走稳定实例。这一步在 LoadBalancer 里的实现思路是在网关的 GlobalFilter 里把请求头的信息设置到Request的 hint 字段中然后在自定义策略里读取 hint过滤实例。Spring Cloud LoadBalancer已经预留了hint机制默认的DefaultRequest支持携带 hint。网关里要做的就是在过滤链中构造一次RequestServerWebExchange exchange exchangeHolder.get(); String version exchange.getRequest().getHeaders().getFirst(X-Version); RequestDefaultRequest.CacheContext request new DefaultRequest( new DefaultRequest.CacheContext(serviceId, mapHint(version)) );策略里再根据request.getHint()去匹配带有对应 metadata 的实例。这套机制的好处是负载均衡策略不用硬编码业务逻辑灰度规则可以通过请求头动态变化。不过灰度分流一旦涉及实例过滤就要考虑“灰度实例不存在时怎么办”的兜底策略比如全部打到稳定实例还是直接报错。我自己的经验是默认走稳定实例避免灰度版本未发布时请求直接把用户带崩。4. 组合拳超时、重试、熔断与负载均衡的配合自定义负载均衡解决的是“选谁”的问题但选完之后请求可能因为后端超时、故障而失败。这一节把这几个关键点和 LoadBalancer 的配合讲清楚。4.1 HttpClient 全局超时和单路由超时Gateway 默认用 Netty 的 HttpClient 转发请求它的超时配置分为两段connect-timeout建立 TCP 连接的超时单位是毫秒response-timeout等待响应整体的超时注意它不是单纯的读超时而是每次请求从发起到收到响应的总时间要用 Duration 格式写。全局配置spring: cloud: gateway: httpclient: connect-timeout: 1000 response-timeout: 5s单条路由可以覆盖全局配置比如有的后端接口本身就是慢接口给它单独放宽spring: cloud: gateway: routes: - id: order-service uri: lb://order-service predicates: - Path/api/order/** metadata: connect-timeout: 2000 response-timeout: 10s注意单路由的 metadata 里connect-timeout单位是毫秒response-timeout也是数值形式表示毫秒和全局配置写Duration的方式不一样。我第一次配的时候这里写成了5s结果启动直接报类型转换错误。4.2 Retry GatewayFilter 与 LoadBalancer 的协作关系网关层面的重试由Retry GatewayFilter提供。它和控制“同一个实例失败后是否重试”的 LoadBalancer 重试spring.cloud.loadbalancer.retry.enabled是两码事别混在一起。Retry GatewayFilter默认 order 是 -1排在过滤器链的最前面。它重试的时候整条过滤器链会重新执行也就是说后面的ReactiveLoadBalancerClientFilter会再次做负载均衡选择实例。所以你配置Retry后如果第一次打到了故障实例第二次重试有可能会选到另一个实例天然实现了“自动切换实例重试”。一个典型的配置filters: - name: Retry args: retries: 2 statuses: BAD_GATEWAY, SERVICE_UNAVAILABLE methods: GET backoff: firstBackoff: 100ms maxBackoff: 500ms factor: 2这里backoff表示重试退避第一次重试等 100ms之后间隔指数增长最多到 500ms。statuses限制了只有 502、503 这类状态码才触发重试methods限制了只有 GET 请求才重试。这是极其关键的一点POST、PUT 这类写操作默认都不建议开重试否则网关在超时后重发请求后端可能已经处理成功结果造成重复下单、重复扣款。我见过不止一次因为给 POST 开了重试导致的线上故障后面避坑章节会详细说。4.3 CircuitBreaker 兜底别让单点拖垮网关负载均衡只保证“按策略选实例”一旦某个实例持续故障它不会主动把请求绕开。这时候需要熔断兜底。Gateway 内置了CircuitBreaker GatewayFilter内部封装了 Resilience4j 的响应式实现。配置在路由的 filters 里filters: - name: CircuitBreaker args: name: orderServiceCB fallbackUri: forward:/fallback/order当上游连续失败达到熔断阈值后续请求不会再向后端发起真正的负载均衡转发而是直接走fallbackUri指向的本地兜底接口。这个 filter 同样通过GlobalFilter实现并且 order 比较靠前所以可以把整个 LB 选择、请求转发的过程包裹在熔断保护范围内。Resilience4j 的参数可以在配置里细化resilience4j: circuitbreaker: instances: orderServiceCB: sliding-window-size: 20 failure-rate-threshold: 50 minimum-number-of-calls: 5 wait-duration-in-open-state: 10s这套组合的效果是负载均衡负责选实例重试负责瞬时抖动熔断负责持续故障。三者配合网关才能在后端不稳定的情况下保持基本可用。4.4 一组实战可用的完整配置把上面的内容拼起来一条内部服务路由的完整配置大致长这样spring: cloud: gateway: httpclient: connect-timeout: 1000 response-timeout: 5s pool: max-connections: 500 acquire-timeout: 3s routes: - id: order-service uri: lb://order-service predicates: - Path/api/order/** filters: - StripPrefix1 - name: CircuitBreaker args: name: orderCB fallbackUri: forward:/fallback/order - name: Retry args: retries: 2 statuses: BAD_GATEWAY methods: GET backoff: firstBackoff: 100ms maxBackoff: 500ms factor: 2这套配置我实测下来在中等流量下表现稳定。max-connections500是指 Netty 连接池的最大连接数不是线程数别和 Tomcat 的线程池概念搞混。连接池太小高峰期会出现大量请求排队等连接表现为接口 RT 飙高但后端负载很低。5. 线上避坑实录这些问题我全都遇到过这一节的内容是你在官方文档和 Demo 项目里基本看不到的全是我自己从故障排查中逼出来的经验。5.1 Spring MVC 依赖和 WebFlux 底座打架这个坑基本是新手必踩。Gateway 基于 WebFlux底层是 Reactor Netty如果项目里不小心引入了spring-boot-starter-web启动时大概率报Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway.更隐蔽的情况是某些公共模块里间接带了spring-boot-starter-web排查起来特别费劲。我的处理办法是在新项目里第一时间把spring-boot-starter-web排除掉dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId exclusions exclusion groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /exclusion /exclusions /dependency同时养成好习惯在 Gateway 项目里申请新依赖的时候先看一眼传递依赖里有没有spring-webmvc。5.2 No instances available排查路径No instances available for order-service这个报错几乎人人都会遇到但原因五花八门。我按出现频率排列一下排查顺序LoadBalancer 依赖缺失。lb://解析的时候根本没注册负载均衡器直接报没有实例。先检查spring-cloud-starter-loadbalancer在不在。服务名不一致。路由里写的是order-serviceNacos 里注册的是order_server对不上。注意下划线和连字符的问题Nacos 注册时服务名一般原样保留。namespace 不一致。网关和微服务不在同一个 Nacos 命名空间互相看不到。很多公司一台 Nacos 上部署多套环境这个错最容易发生。缓存未刷新。实例刚下线LoadBalancer 本地还留着旧列表最多延迟 35 秒。如果是发布时的临时报错等缓存过期基本就恢复了。排查时最有用的命令是直接看 Nacos 控制台确认服务列表里有没有实例以及实例数量是否和预期一致。控制台有、网关却报没有那 90% 是命名空间或者服务名的问题。5.3 自定义策略怎么就不生效自定义负载均衡策略在 Spring Cloud 里生效的路径比较绕我踩过最大的坑就是配置类被主扫描扫到。我一开始图省事把OrderServiceLoadBalancerConfig直接放在主启动类同一个包下还加了Configuration。结果启用之后所有通过网关转发的服务都走了自定义加权策略连首页这种静态服务也被强行解析权重流量分配完全乱套。后来才搞明白LoadBalancerClientFactory是一个NamedContextFactory它会为每个服务名创建独立的子上下文。LoadBalancerClient(name order-service, configuration XxxConfig.class)的作用是把XxxConfig这个配置类专门应用到order-service的子上下文里。一旦这个类同时被主应用上下文扫描到了它就“既是全局的又是局部的”优先级变得非常乱。正确的做法是配置类放在主启动类扫描范围之外比如com.xxx.gateway.config.lb子包且去掉Configuration注解或者用Configuration但要确保它绝对不在ComponentScan的扫描路径内。5.4 重试引发的重复提交问题这是我认为 Gateway 整合 LoadBalancer 里最危险的一个坑。Retry GatewayFilter重试时会重新执行整条过滤器链ReactiveLoadBalancerClientFilter会再次选择实例。也就是说第一次请求打到实例 A 超时重试可能打到实例 B实例 B 会再次执行业务。如果这个接口是创建订单、扣减库存这类写操作用户可能根本没提交第二次系统却生成了两笔订单。我这边出过一次类似的 P1 故障一个内部接口因为数据库连接池打满大量请求超时网关重试两轮直接把数据库连接池打到彻底无响应。事后复盘重试对写操作是致命的。我的建议很简单filters: - name: Retry args: methods: GET生产环境对写操作一律不配重试。如果确实需要做消息级别的补偿那是业务层面的事不应该靠网关无脑重放请求。还有一种情况LoadBalancer 自带的重试开关spring.cloud.loadbalancer.retry.enabled它控制的是对同一个实例的请求失败后是否重试我一般保持默认关闭。因为一旦开启同一个故障实例会被反复打闷棍网关线程很容易被拖住。5.5 从 http:// 改成 lb:// 后 503这个坑也很典型。有些人刚开始图简单路由里写的是uri: http://127.0.0.1:8081直连一个固定实例。后来服务扩容了改成lb://order-service结果请求直接 503。原因多半是注册中心里服务名对不上或者 LoadBalancer 依赖没引。我建议在改动路由协议之后先对单服务做一个最小验证确认实例列表能正常拉取再切流量。不要拿线上全量流量去赌“肯定没问题”。另外http://和lb://两种写法混用时过滤器链的行为完全不同。http://会直接走 Netty 转发不经过负载均衡lb://才会触发实例选择。如果你在一条路由里想“先负载均衡失败后降级到固定地址”那要靠自定义过滤器实现Gateway 原生配置不支持这种动态切换。最后说一个我亲历的发布问题新节点扩上去之后Nacos 里明明能看到两个实例但网关流量就是一直打在旧实例上新实例一毛钱流量都接不到。排查了半天发现问题出在两处——第一是LoadBalancerCache的 TTL默认 35 秒新实例不是立刻生效第二是我在自定义策略里读权重的 key 和当前 Nacos 客户端版本实际写入的 key 不一致解析权重失败全部回了默认权重 1看似随机实则完全没有达到预期效果。后来我把权重读取逻辑改成“先动态探测 metadata 里的 key探测不到再从原生 Nacos 实例对象取”并把缓存 TTL 调成 10 秒发布后流量基本在两轮请求内就能完全均衡。这种问题你翻官方文档是翻不到的只能在真实环境里靠日志一层层逼出来。希望这篇从基础到进阶的整合过程能帮你少走一点我走过的弯路。

相关新闻

一维插值实战指南:9种缺测数据填充方法对比与Python实现

一维插值实战指南:9种缺测数据填充方法对比与Python实现

做数据处理最烦的时刻,不是数据量太大,而是曲线中间莫名其妙空了一截。温度记录断档、传感器掉线、清洗时误删一段……前后数据都好好的,唯独中间留着缺口。这时候唯一能做的,就是拿已有数据把缺口“缝”回去,也就是做…

2026/10/13 13:23:57 阅读更多 →
LSTM时序建模全流程:特征选择、滑窗与可视化避坑指南

LSTM时序建模全流程:特征选择、滑窗与可视化避坑指南

前阵子收到一份Excel,打开一看,6列特征加1列标签,数据一行一行排得整整齐齐——这正好是个需要“特征选择 LSTM建模 结果可视化”三件套走一遍的典型任务。类似格式的数据我见得不少,很多同学的常规操作是读进来、归一化、丢给L…

2026/10/13 13:23:57 阅读更多 →
FIFO深度计算公式与工程取舍:从突发缓冲到异步同步约束

FIFO深度计算公式与工程取舍:从突发缓冲到异步同步约束

1. 先想清楚FIFO深度在防什么——突发窗口内的积压峰值1.1 FIFO就是读写速率不匹配时的一只缓冲桶FIFO深度设多大?这是我见过被“拍脑袋”定得最多的一个参数。很多人画框图时随手写个32,被问为什么不是16或64,只能回一句“够了吧”。直到某次…

2026/10/13 13:23:57 阅读更多 →

最新新闻

统一多模型API网关litellm实战:从配置到生产部署

统一多模型API网关litellm实战:从配置到生产部署

这段时间我一直在做一个内部的多模型调度平台,见得最多的一个词就是 litellm。它不是一个具体的大模型,也不是某个框架的插件,而是一个把各种模型 API 统一成一套格式的网关层工具。说白了,你只需要学会接一个服务,剩下…

2026/10/13 13:58:46 阅读更多 →
高中化学实验题满分攻略:从操作原理到误差分析的系统梳理

高中化学实验题满分攻略:从操作原理到误差分析的系统梳理

1. 为什么高中化学实验题总是“看着全会、做题全废”先抛个问题:你有没有过这种经历——课本上的实验都读了一遍,氯气的实验室制法、氨气的喷泉实验、酯化反应的装置图,看着都觉得挺简单,可一到考试,装置图给你一个洗气…

2026/10/13 13:58:46 阅读更多 →
AI写代码的质量保卫战:规范、评审与风险测试三重防线

AI写代码的质量保卫战:规范、评审与风险测试三重防线

1. 这不是又一个“AI写代码”的热闹话题,而是工程团队正在真实发生的质量保卫战最近在几个技术社区和内部分享会上,我反复听到同一个问题:“我们上了Copilot、接入了CodeWhisperer、也试了各种本地大模型,但代码质量没变好&#x…

2026/10/13 13:58:46 阅读更多 →
PyCharm安装配置避坑指南:跨平台解释器绑定与IDE工程化预检

PyCharm安装配置避坑指南:跨平台解释器绑定与IDE工程化预检

简介:本资源是一份面向Python初学者与转行开发者的PyCharm零基础安装与入门配置指南,聚焦解决“不会装、不敢用、配不熟”三大实操痛点。PDF文档结构清晰,完整覆盖Windows、macOS、Linux三大主流系统下的下载选择、分步安装、首次配置&#x…

2026/10/13 13:58:46 阅读更多 →
SpringBoot+Vue+SpringCloud微服务实战:社区帮扶平台从单体重构到分布式

SpringBoot+Vue+SpringCloud微服务实战:社区帮扶平台从单体重构到分布式

前阵子帮老家社区做了一个邻里互帮互助的小平台,一开始就打算用单体应用糊弄过去,结果需求一多彻底压不住——又要搞门户展示、又要做帮扶工单流转、还要上可视化大屏给街道看数据,再加上志愿者积分、公告通知、用户认证这些模块,…

2026/10/13 13:58:46 阅读更多 →
PCL 1.13.0 与 Qt 6 集成实战:从 CMake 到点云可视化

PCL 1.13.0 与 Qt 6 集成实战:从 CMake 到点云可视化

简介:面向需要在Qt6框架中集成PCL 1.13.0进行点云处理开发的工程师与研究者,这份资源提供了完整的PCL编译产物与配套依赖。包内含约2.4万个文件,以hpp/h头文件、lib静态库、dll动态库及cmake配置文件为主,另含3rdParty第三方依赖、…

2026/10/13 13:57:45 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →