ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Nacos 负载均衡原理与实战:客户端选实例、权重与集群排查

Nacos 负载均衡原理与实战:客户端选实例、权重与集群排查 很多人第一次听到「Nacos 实现服务间的负载均衡」这个说法会下意识地把它和 Nginx 画上等号——前面挂个 Nacos请求进来它帮忙分发到后面的实例上。我早些年也这么理解直到有一次排查「新发布的实例一台流量都接不到」的问题把整条链路从调用方一直拉到注册中心才发现 Nacos 在这件事里的位置和 Nginx 完全是两码事。它不转发任何一个字节的业务流量只负责把「现在有哪些实例可用、各自什么状态」这份名单及时、准确地送到每个调用方的进程里真正决定这一次请求落到哪台机器上的是调用方进程内部那个几十行的负载均衡器。把这个分工搞明白后面所有关于权重、集群、缓存、下线延迟的问题基本都能自己推导出答案。这篇内容我打算把 Nacos 侧和客户端侧两条线拆开讲从实例模型讲到落地配置再到线上最常见的几类「负载不均」投诉的排查路径适合正在用 Spring Cloud Alibaba 或 Dubbo 做微服务、被流量分配问题折腾过的同学。1. 先把概念掰正Nacos 的负载均衡发生在哪一层1.1 把 Nacos 当 Nginx 用是第一个要跳出来的坑Nginx 做的是服务端负载均衡调用方只知道 Nginx 的地址请求先到 NginxNginx 拆包、按 upstream 策略选一台后端、再把请求转发过去。整条链路上多了一跳Nginx 本身是流量咽喉所有并发都要从它身上过所以你得考虑它的带宽、连接数、集群化甚至要考虑它挂了怎么办。Nacos 走的是另一条路。调用方启动时把自己的地址注册进 Nacos同时在本地订阅自己关心的那些服务。当被调服务扩容、缩容、发布、宕机时Nacos 把最新的实例列表推给所有订阅者。调用方手里有了一份完整的名单接下来选谁、怎么选全是本地算出来的。这叫客户端负载均衡。这两个方案没有绝对优劣但差异非常实在。客户端负载均衡少了一跳网络开销调用方自己就是决策者没有中心瓶颈某个调用方觉得某台机器慢它可以自己少发一点代价是每个语言、每个框架都得有一套对应的客户端实现策略升级往往要跟着业务发版。理解这个差异之后你就会明白为什么 Nacos 控制台上改个权重得等一小会儿才生效——它不是改了 Nginx 的配置而是改了名单再推送出去中间还隔着好几层缓存。还有一点必须说清楚Nacos 严格意义上不提供「服务端负载均衡」。有人会拿它的权重说事其实权重只是实例的一个属性字段写进名单里由客户端去解释。控制台改权重之所以「有效果」是因为客户端订阅到了 metadata 变化然后自己按新的权重重算了一遍。1.2 一次调用的完整链路订阅、缓存、选实例、发起连接把一次跨服务调用的完整链路摊开你会看到四个阶段每一步都有缓存每一步都可能出问题。注册阶段服务实例启动nacos-client 向注册中心提交自己的 IP、端口、权重、集群名和自定义 metadata。Nacos 2.x 之后这条通道走的是 gRPC 长连接不再是早期的 HTTP 短请求。订阅阶段调用方启动时订阅目标服务Nacos 服务端在实例列表变化时通过长连接主动推送1.x 时代是 UDP 推送加定时轮询兜底2.x 换成了 gRPC 双向流实时性好了很多。客户端收到推送后更新内存里的ServiceInfoHolder同时写一份磁盘快照到${user.home}/nacos/naming/${namespace}目录下用于下次启动时的容灾。选实例阶段这是负载均衡真正发生的地方。Spring Cloud 体系下ServiceInstanceListSupplier负责提供候选列表ReactorServiceInstanceLoadBalancer负责从中挑一个。这里藏着很多人忽略的一层Spring Cloud LoadBalancer 自己带了一个 Caffeine 缓存默认 TTL 是 35 秒。也就是说哪怕 Nacos 在 100 毫秒内就把「某台实例已下线」推到了客户端客户端的 nacos-client 缓存也更新了负载均衡器仍可能在接下来的 35 秒里继续把那台机器当候选。发起连接阶段拿到实例的 host 和 port用 RestTemplate、WebClient 或 Feign 发请求。这一步的坑在连接池上后面会展开。1.3 命名空间、分组、集群三层过滤漏斗Nacos 的服务发现有三个维度的隔离理解它们对负载均衡范围的限定比记住任何配置项都重要。命名空间namespace是最外层的硬隔离。不同 namespace 下的服务互相不可见所以负载均衡这件事根本不会跨 namespace 发生。生产、预发、测试各占一个 namespace是最省心的做法。分组group是业务维度的逻辑隔离默认DEFAULT_GROUP。同一服务名在不同 group 下是两份独立的数据客户端只会订阅自己配置的那个 group。集群cluster就是为负载均衡量身定做的了。同一个服务可以把自己的实例打上不同的 cluster 标签比如bj-zone-a、sh-zone-b客户端在选实例时优先在同 cluster 里挑同 cluster 一个都没有才退回到全量。跨机房部署时这就是免费拿到的就近访问能力。最后还有metadata一个完全开放的键值对容器。version、env、zone、weight你想塞什么都可以它是灰度发布、自定义路由策略最主要的落地载体。Nacos 本身不解释这些内容但你的自定义负载均衡器可以。2. 实例权重与元数据控制台上那几个旋钮的真实含义2.1 权重不是百分比加权随机的算法是这样跑的Nacos 里每个实例都有一个weight字段类型是 double默认 1.0控制台可以直接改。很多人第一反应是把它当百分比用看到两个实例就把权重设成 50 和 50看到三个就设 33、33、34其实完全没必要。主流实现走的是加权随机把所有候选实例的权重累加起来得到总权重生成一个[0, totalWeight)区间内的随机数然后从头开始累减落在谁的区间里就选谁。所以权重 2 和权重 1 的两个实例被选中的概率大约是 2:1写 200 和 100 是同样的效果。权重配置实际分流比例说明1.0 / 1.050% / 50%默认状态等概率2.0 / 1.0约 66.7% / 33.3%相对比例不是百分比100 / 100 / 10033.3% / 33.3% / 33.3%写多少都行看相对值0 / 1.00% / 100%权重 0 表示不承接流量关于权重 0有个细节值得说一句在主流版本的NacosBalancer实现里权重为 0 的实例由于区间长度为 0实际上不会被选中等价于「摘流量」。但不同 SDK 版本对这个边界的处理未必一致而且万一所有实例的权重都被改成了 0实现通常会退化成等概率随机反而全都接流量了。所以别把权重 0 当成可靠的下线开关真要摘流量就直接把实例下线。还有一点容易踩权重是实例级别的不是接口级别的。同一台机器上/order/create和/order/query的负载特征可能差十倍但 Nacos 只认一个权重值。真要按接口分流只能自己写负载均衡器。2.2 clusterName 的「同机房优先」是怎么生效的spring.cloud.nacos.discovery.cluster-name这个配置项默认值是DEFAULT。它决定了本实例注册到 Nacos 时被归到哪个集群下。客户端的NacosLoadBalancer在选择实例时逻辑大致是先从候选列表里筛出和本机同 cluster 的实例如果筛出来的结果为空比如本机配置的 cluster 名和服务端的对不上就回退到使用全部实例然后在这个子集上做加权随机。跨机房部署时把所有机器按物理机房或可用区打上 cluster 标签天然就拿到了「优先本地、故障时跨机房兜底」的行为不需要额外写代码。注意同一服务的不同实例cluster-name 必须保持一致的口径。我见过一个真实案例运维在扩容时把新机器的配置从BJ抄成了DEFAULT结果新实例永远排在候选池的后面扩容了五台机器流量几乎没变化排查了两个小时才在控制台的实例视图里发现集群名不一致。上线前一定要把控制台的服务详情页打开逐台核对集群名。另外要澄清一个误解cluster 和健康检查没有任何关系。机器真的挂了摘除走的是心跳超时或服务端探测那套机制改 cluster 名解决不了任何问题。2.3 临时实例与持久实例决定了故障摘除的速度Nacos 的实例分两种类型这个选择直接决定了「实例挂掉后多久才会被摘出名单」。临时实例ephemeraltrue是默认值。客户端每 5 秒上报一次心跳服务端 15 秒收不到心跳就把实例标记为不健康30 秒收不到就直接从列表里删掉。这套机制靠客户端自觉好处是摘除快坏处是它走的是 Distro 协议AP 模型集群各节点之间是异步同步的极端网络分区的情况下不同 Nacos 节点看到的实例列表可能短暂不一致客户端连到哪个节点就拿到哪一份数据。持久实例ephemeralfalse则需要服务端主动探测通过 TCP、HTTP 或者 MySQL 探活的方式判断实例是否可用。它走 Raft 协议CP 模型数据一致性强实例信息会持久化即使所有客户端都失联也不会被删掉。代价是摘除速度取决于探测周期网络抖动时也不会因为心跳丢失就误摘健康实例。选哪个我的经验是这样容器化环境、实例生命周期短、能保证优雅下线的用临时实例网络环境不稳、跨云跨地域部署、或者实例本身不方便主动上报心跳的比如某些老服务只提供 TCP 端口没人写心跳逻辑用持久实例。还有一件事必须提容器里跑的服务如果被kill -9是没有机会主动去注销的得等服务端 30 秒心跳超时。所以 K8s 环境下一定要配preStop钩子留出十几秒窗口让进程完成注销和等待。这个后面在第 6 节还会细说。3. Spring Cloud Alibaba 落地从依赖到 NacosLoadBalancer3.1 依赖与最小可用配置先说依赖。spring-cloud-starter-alibaba-nacos-discovery是必须的同时建议把spring-cloud-starter-loadbalancer显式加进来不要指望它被传递依赖带进来——版本对齐出问题时最典型的表现就是LoadBalanced的 RestTemplate 报No instances available或者压根找不到ServiceInstanceListSupplier的实现。dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-loadbalancer/artifactId /dependency配置部分下面这份是能直接用的最小集合spring: application: name: order-service cloud: nacos: discovery: server-addr: 10.0.0.11:8848 namespace: prod group: ORDER_GROUP cluster-name: bj-zone-a ephemeral: true metadata: version: v2 loadbalancer: nacos: enabled: true cache: ttl: 5s这里有两个配置项值得单独点出来。spring.cloud.loadbalancer.nacos.enabledtrue是启用 Nacos 自己那套负载均衡逻辑的开关不打开的话默认走的是 Spring Cloud 的轮询策略完全不看权重也不看集群。spring.cloud.loadbalancer.cache.ttl默认是 35 秒把它压到 5 秒能显著缩短下线延迟代价是实例列表刷新变频繁实例规模特别大的集群要权衡一下。3.2 从 Ribbon 迁到 LoadBalancer最容易漏掉的三个点Spring Cloud 2020.0 之后 Ribbon 被移出ribbon.*开头的配置项全部失效一个都不生效。迁过来的同学最常在这三个地方翻车。第一超时配置的归属变了。Ribbon 时代ribbon.ReadTimeout一把梭现在超时属于 HTTP 客户端自己的事。用 RestTemplate 就得配ClientHttpRequestFactory的 readTimeout 和 connectTimeout用 Feign 就得配feign.client.config.服务名.readTimeout。很多人改完依赖发现调用一超时就卡很久就是因为老的ribbon.ReadTimeout已经没人读了实际用的是框架默认的超时。第二重试的依赖变了。负载均衡重试现在依赖 Spring Retry需要在 classpath 里有spring-retry配置项是spring.cloud.loadbalancer.retry.*。没有这个依赖重试配置写了也不生效而且不会有明显报错。第三LoadBalanced的 RestTemplate 不能重复定义。项目里如果已经有一个用来调外部固定地址的 RestTemplate再加一个带LoadBalanced的注入时就会因为类型相同而冲突。常见的解法是给带负载均衡的那个加Qualifier区分或者干脆用LoadBalancerClient手工构建。3.3 真正跑起来需要检查的连通性项Nacos 2.x 引入了 gRPC 长连接之后端口从「一个」变成了「一组」。这是容器部署时最经典的坑。端口偏移用途8848主端口控制台、OpenAPI、HTTP 请求9848主端口 1000客户端 gRPC 连接9849主端口 1001服务端集群间 gRPC 同步如果 Docker 部署时只-p 8848:8848服务注册会失败客户端日志里会反复打印client not connected, current status: STARTING或者Server check fail。这种情况不要怀疑代码先去telnet 10.0.0.11 9848确认端口通不通。还有个排查利器值得一提客户端的本地缓存目录。${user.home}/nacos/naming/${namespace}下会有一份份 JSON 快照文件文件名就是服务名内容是最近一次拿到的实例列表。判断「客户端到底拿到的是什么数据」时直接看这个文件比看日志快得多。最后一个提醒spring.cloud.nacos.discovery.naming-load-cache-at-start这个开关打开后启动时会先读磁盘缓存能在 Nacos 短暂不可用时保证服务能起来但也意味着可能拿到一批早就下线的实例。除非有明确的容灾诉求否则建议保持默认的 false。4. 自定义负载均衡灰度、动态调权与缓存亲和4.1 为什么要自己写内置策略覆盖不到什么NacosLoadBalancer做的是「同 cluster 优先 加权随机」这套逻辑能覆盖百分之七八十的场景但下面这几类需求它接不住按请求头里的用户标识做灰度让 1% 的流量走新版本按接口粒度分别控制流量比例根据实时的响应时间动态调整权重用一致性哈希把同一用户的请求固定到同一台机器上吃满本地缓存。自定义的入口是实现ReactorServiceInstanceLoadBalancer接口或者在NacosLoadBalancer基础上继承、只覆盖选择逻辑。除非你有非常充分的理由不要直接去改框架源码升级时的代价太大。注册自定义策略有两种粒度LoadBalancerClient(name order-service, configuration GrayConfig.class)针对单个服务LoadBalancerClients(defaultConfiguration GrayConfig.class)针对所有服务。这个 Configuration 类不能被ComponentScan扫到否则会变成全局默认配置影响其他服务。4.2 一个可用的灰度策略实现思路思路很简单从请求上下文里取出灰度标识去实例的 metadata 里匹配version标签匹配到的组成一个子池在子池里做加权随机子池为空则回退全量。public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer { private final ObjectProviderServiceInstanceListSupplier supplierProvider; private final AtomicInteger position new AtomicInteger(new Random().nextInt(1000)); Override public MonoResponseServiceInstance choose(Request request) { ServiceInstanceListSupplier supplier supplierProvider .getIfAvailable(NoopServiceInstanceListSupplier::new); return supplier.get(request).next() .map(list - pick(list, request)); } private ResponseServiceInstance pick(ListServiceInstance all, Request request) { if (all.isEmpty()) { return new EmptyResponse(); } String target resolveVersion(request); ListServiceInstance pool all; if (target ! null) { ListServiceInstance matched all.stream() .filter(i - target.equals(i.getMetadata().get(version))) .collect(Collectors.toList()); // 灰度池为空时回退全量避免打错标签直接 503 if (!matched.isEmpty()) { pool matched; } } return new DefaultResponse(weightRandom(pool)); } private String resolveVersion(Request request) { if (request.getContext() instanceof RequestDataContext ctx) { return ctx.getClientRequest().getHeaders().getFirst(X-Service-Version); } return null; } }resolveVersion这段有个前提请求头必须能透传到负载均衡器。Feign 和 WebClient 走负载均衡时原始请求的 Header 是带过来的可以直接读RestTemplate 配合LoadBalanced时建议用一个ClientHttpRequestInterceptor把 Header 塞进请求上下文否则读不到。加权随机的实现要注意用ThreadLocalRandom而不是Random在高并发下前者的竞争开销明显更低。4.3 兜底、线程安全与被忽略的执行频率写自定义负载均衡器有三件事我认为比策略本身更重要。兜底一定要有。上面代码里那句「灰度池为空时回退全量」不是可有可无的优化而是保命的。灰度标签打错、新版本实例尚未启动、元数据配置遗漏任何一种情况都可能让候选池变成空的。空池子如果不回退结果就是整个服务直接 503一个标签问题演变成一次故障。实例列表为空必须返回EmptyResponse交给框架去抛异常千万不要在里面list.get(0)也不要在里面打空指针。负载均衡器是整个调用链上最热的代码路径这里抛出的任何异常都会被放大到所有请求。要清楚这段代码的执行频率。choose()每次发起调用都会被触发但它拿到的列表来自ServiceInstanceListSupplier而 supplier 背后有 Caffeine 缓存兜着默认 35 秒才真正刷新一次。所以在choose()里做滑动窗口统计、读 Redis、查配置中心这类重活是会把性能拖垮的。要做动态调权统计应该放在独立的定时任务里算好负载均衡器只读结果。5. Dubbo Nacos 那条线上的负载均衡5.1 Dubbo 的目录服务如何消费 Nacos 实例如果你的技术栈是 Dubbo 而不是 Spring Cloud那 Nacos 在这里扮演的角色是注册中心负载均衡由 Dubbo 自己完成是另一套完全独立的机制。Dubbo 的调用链路上有三个角色Directory负责从注册中心拿到 Invoker 列表并维护Router负责按规则过滤比如标签路由、条件路由LoadBalance负责从过滤后的列表里选一个。Nacos 只参与第一步。配置上是一条 URLdubbo.registry.addressnacos://10.0.0.11:8848?namespaceprod。需要留意的是Dubbo 注册到 Nacos 的实例默认也是临时实例心跳由 Dubbo 框架自己维护和 Spring Cloud 的心跳参数是两套东西不要混着调。Dubbo 3 之后推的应用级服务发现改变了注册粒度以前每个接口在 Nacos 里都是一份独立数据现在一个应用只注册一份实例列表接口信息通过 Metadata Center 上报。对负载均衡的影响是候选列表变少、变稳定了但前提是 Metadata Center 得配好否则会出现「能发现实例但不知道实例提供了哪些接口」的问题。5.2 内置策略的适用场景对比Dubbo 自带的策略比 Spring Cloud 丰富得多选对了能省掉很多自研工作。策略核心逻辑适合什么场景random加权随机默认策略通用场景实例配置和性能比较均匀roundrobin平滑加权轮询需要严格按权重比例分配流量leastactive选当前并发调用数最少的后端处理耗时差异大慢机器自动少接流量consistenthash相同参数始终落到同一实例有本地缓存或需要会话粘性shortestresponse滑动窗口统计响应时间选最快的对尾延迟特别敏感的核心链路leastactive是我个人最推荐在中大型集群里默认打开的。它的自适应能力很强一台机器如果开始变慢它的在途请求数会自然堆积leastactive就会自动少分流量等于是一个零成本的过载保护。consistenthash有两个坑要特别注意。一是默认对第一个参数做哈希参数最好选稳定、区分度高的字段比如 userId不要用时间戳这种每次都变的二是哈希环上的分布依赖参数取值的多样性如果你的参数只有几十种取值流量会严重倾斜这时候反而不如用一致性哈希的虚拟节点数调大一些或者换回加权随机。5.3 权重、warmup 与 leastactive 的联动Dubbo 的服务端权重通过dubbo.provider.weight配置默认 100最终会写进注册到 Nacos 的实例 metadata 里。但最终参与计算的权重不完全是这个值还要经过warmup的修正。warmup的默认值是 10 分钟。实例刚启动时权重会按启动时长 / warmup这个比例打折也就是刚起来的头一分钟实际权重可能只有满值的十分之一。这个设计非常聪明——刚启动的 JVM 还在解释执行、JIT 没预热、本地缓存是空的如果一上线就接满流量很容易被打到假死然后触发连锁反应。所以千万不要为了「让新机器赶紧分担压力」去把 warmup 调到 0。leastactive在计算时会结合每个 Invoker 的 active 计数如果 active 相同则退化成加权随机。这里有个隐藏前提active 计数只有在方法被标记为actives限制或者用了ActiveLimitFilter时才准确否则可能是粗略统计。如果你打算重度依赖这个策略建议在压测环境里验证一下 active 计数的准确性。6. 上线之后最常见的四类投诉与排查路径6.1 实例已下线流量还在打这是最高频的问题也是最需要按链路顺序排查的。千万不要一上来就质疑 Nacos因为问题绝大多数出在客户端侧。第一步看 Nacos 控制台。打开服务详情页确认那台实例是不是真的消失了或者已经标成不健康。如果实例还健康地挂在那儿说明进程没注销成功、心跳还在发那问题在服务端进程本身不在负载均衡。第二步看客户端本地缓存。去${user.home}/nacos/naming/${namespace}下打开对应的快照文件看这台实例是否还在里面。如果还在说明 nacos-client 没有收到推送检查长连接是否正常、网络是否稳定。第三步看负载均衡器的缓存。nacos-client 更新了不代表负载均衡器更新了。spring.cloud.loadbalancer.cache.ttl默认 35 秒这个值在排查时经常被忘掉。把它临时调到 5 秒如果现象消失那就定位到了。第四步看连接池。如果前三步都正常但现象仍在那就是底层 HTTP 连接池里保持了长连接旧连接被复用。这时候需要给连接池设置setConnectionTimeToLive让空闲连接定期失效重建。根因往往在优雅下线没做。正确的顺序是先调用namingService.deregisterInstance()主动注销然后等待一段时间至少覆盖掉下游的负载均衡缓存 TTL 加上心跳超时周期20 到 30 秒比较稳妥最后才停进程。K8s 环境下把这段逻辑放在preStop里配合terminationGracePeriodSeconds给足时间。这一套做下来用户的感知就是平滑的。6.2 权重改了但比例没变遇到这个问题按下面这个顺序问自己三个问题基本三分钟内能定位。第一负载均衡器用的是哪个实现这是最高频的原因。spring.cloud.loadbalancer.nacos.enabled没打开的话走的是默认的轮询策略压根不看权重改上天也不会有效果。第二容器里跑的是哪个版本的代码老版本可能还依赖 Ribbonribbon.*配置已失效改配置自然无用。查一下依赖树里有没有残留的 ribbon 包。第三所有实例的权重是不是都还是 1.0如果总权重为 0实现通常会退化成等概率随机表现就是「怎么改都没反应」。顺带说一句改完权重之后别立刻验证给推送和缓存一个刷新周期等个几十秒再看监控上的 QPS 分布否则容易误判。6.3 慢实例拖垮整条链路加权随机有个天生的盲点它不看响应时间。只要实例是健康的它就一直拿 1/N 的流量。当某台机器开始变慢但还没挂的时候它的连接池和线程会逐渐被占满然后依赖它的上游开始排队一个局部问题迅速演变成大面积超时。三种解法复杂度递增。最省事的是用熔断降级。HTTP 侧上 Resilience4j 的 TimeLimiter或者用 Sentinel 配慢调用熔断把慢调用比例超过阈值的调用直接熔断掉。要注意熔断的粒度是「调用方到某个实例」的关系不是全局摘除所以每个调用方都得配。再进一步是让负载均衡器自己感知延迟。Dubbo 那边直接换leastactive或shortestresponse就行Spring Cloud 这边要自己写在独立的定时任务里做滑动窗口统计按 P99 反向调权负载均衡器只读算好的权重。最不应该做的是「手动把慢机器的权重调低」当临时手段。这么做只是在压制症状慢的根因还在而且权重是静态的你调低之后流量会集中到其他机器上等于把风险转移了出问题时反而更集中。6.4 集群扩容后流量倾斜扩容本该让每台机器的压力下降结果发现新机器几乎没拿到流量或者新机器一上线就被打满。两种情况原因完全不同。没拿到流量先看容量规划。RestTemplate 底层如果用PoolingHttpClientConnectionManagermaxConnTotal和maxConnPerRoute配小了的话新增的实例会因为拿不到连接而几乎接不到请求。maxConnPerRoute至少要大于单个实例能承受的并发数maxConnTotal要大于「实例数 × 单实例并发上限」很多项目的默认值压根不够用。还有一种情况是客户端实例列表刷新有延迟扩容后的头几十秒新实例只拿到少量流量属于正常现象不用管。一上线就被打满基本是 warmup 没配或者被关掉了。Dubbo 有内置的 warmupSpring Cloud 这边没有只能自己在应用启动完成后延迟一小段时间再放开服务或者用元数据标注启动时间在自定义负载均衡器里做降权。我个人在这些年踩过的坑里印象最深的一次是某个核心服务扩容了十台机器监控上负载纹丝不动。查了两个小时最后发现是扩容脚本里的模板变量没替换新实例的 cluster-name 全是默认值而老实例是bj-zone-a客户端的同集群优先策略把它们全过滤掉了。所以每次扩容之后我都会习惯性地打开 Nacos 控制台对着实例列表把所有字段核一遍——花两分钟省两小时。另外还有一个小技巧把spring.cloud.loadbalancer.cache.ttl调到一个你觉得可接受的短值同时在监控里盯住实例列表的刷新频率你会发现很多「流量不均」的抱怨其实只是数据还没刷新到位。
返回列表