bigywater.tech
Spring · 118

Spring Cloud LoadBalancer:接口与默认策略

@LoadBalanced 默认用什么策略?起点是随机的吗?后端挂了会不会自动摘掉?健康检查配了为什么全部请求都失败?新实例上线要多久才有流量?这篇用三个真实后端、一个可以随时改的 DiscoveryClient,在 Spring Boot 4.0.8、Spring Cloud 2025.1.3 上逐项实测。

每次不同
默认轮询的起点:重启 5 次,第一次调用依次落在 b3 b1 b2 b1 b3
1/3
一个后端挂了,默认策略仍把三分之一的请求发给它,30 次里 10 次失败
36 秒
新实例加进实例列表后,默认配置下第一次被调用到的等待时间(cache.ttl=3s 时是 3 秒)

默认策略

实测 · 本机执行输出
  ReactorLoadBalancer = RoundRobinLoadBalancer;ServiceInstanceListSupplier 链 = CachingServiceInstanceListSupplier → DiscoveryClientServiceInstanceListSupplier;LoadBalancerCacheManager = [DefaultLoadBalancerCacheManager]
  连续 12 次调用:b3 b1 b2 b3 b1 b2 b3 b1 b2 b3 b1 b2
  重启应用 5 次,每次第一个调用到的实例:b3 b1 b2 b1 b3

RoundRobinLoadBalancer,实例列表被 Caching 的 supplier 包着,起点随机。

随机策略

实测 · 本机执行输出
  ReactorLoadBalancer = RandomLoadBalancer;ServiceInstanceListSupplier 链 = CachingServiceInstanceListSupplier → DiscoveryClientServiceInstanceListSupplier;LoadBalancerCacheManager = [DefaultLoadBalancerCacheManager]
  300 次调用的分布:{'b1': 111, 'b2': 97, 'b3': 92}
  前 12 次:b2 b2 b1 b3 b3 b2 b3 b1 b3 b1 b3 b1
  对照,默认(轮询)300 次的分布:{'b1': 100, 'b2': 100, 'b3': 100}

声明 RandomLoadBalancer bean 即可,短期分布不均匀。

后端挂了

实测 · 本机执行输出
  默认:30 次调用 {'b1': 10, 'b3': 10, '错误': 10}
  一个错误的例子:ERR ResourceAccessException: I/O error on GET request for "http://orders/id": null
  配了 health-check 但容器里没有 RestClient / RestTemplate bean:ServiceInstanceListSupplier = 没有(getInstance 返回 null)
    6 次调用 {'错误': 6};一个错误的例子:ERR IllegalStateException: No instances available for orders
  ReactorLoadBalancer = RoundRobinLoadBalancer;ServiceInstanceListSupplier 链 = HealthCheckServiceInstanceListSupplier → DiscoveryClientServiceInstanceListSupplier;LoadBalancerCacheManager = [DefaultLoadBalancerCacheManager]
  加上一个 RestClient bean,health-check 1 秒一次:刚启动时的 6 次 b1 b1 b3 b1 b3 b1;3 秒后的 30 次 {'b1': 15, 'b3': 15}

默认不摘除也不重试;health-check 要有 RestClient 或 RestTemplate bean。

实例缓存

实测 · 本机执行输出
  [默认配置] ReactorLoadBalancer = RoundRobinLoadBalancer;LoadBalancerCacheManager = [DefaultLoadBalancerCacheManager]
  [默认配置] 把 b3 加进实例列表之后,过了 36 秒才第一次调用到 b3
  [spring.cloud.loadbalancer.cache.ttl=3s] 把 b3 加进实例列表之后,过了 3 秒才第一次调用到 b3

默认约 35 秒缓存,cache.ttl 可以缩短。

分步演示

LoadBalancer
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。三个后端是 Python 线程里起的 HTTP 服务,/id 返回自己的名字;「挂了」是直接把 b2 的服务关掉(连接被拒绝);调用方是 @LoadBalanced 的 RestClient;缓存实验用的是一个自己写的、可以随时改实例列表的 DiscoveryClient
    • 没有验证:默认缓存时间 35 秒的出处(只测到 36 秒);Caffeine 在 classpath 上时的缓存实现;重试(spring.cloud.loadbalancer.retry)与 avoid-previous-instance;zone-preference、sticky-session、weighted、subset 等其他 supplier;响应式 WebClient 版本的 @LoadBalanced;health-check 对服务恢复后重新加入的速度;实例没有被健康检查摘除时的错误是 ResourceAccessException 而 message 为 null 的原因
    京ICP备2026067502号-1京公网安备11010502063487号