[负载均衡] Ribbon LoadBalancer 之 DynamicServerListLoadBalancer 组件详解

0 序

  • 从1行日志说起:
...

[TID: 7d7f6eacc837482db27bc38bf3502961.1.17698255263050001] [execute-service] [system] [2024/01/31 10:12:06.841] [INFO ] [main] [BaseLoadBalancer] initWithConfig:197  Client: datasource-service instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=datasource-service,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
[TID: 7d7f6eacc837482db27bc38bf3502961.1.17698255263050001] [execute-service] [system] [2024/01/31 10:12:06.852] [INFO ] [main] [DynamicServerListLoadBalancer] enableAndInitLearnNewServersFeature:222 Using serverListUpdater PollingServerListUpdater
[TID: 7d7f6eacc837482db27bc38bf3502961.1.17698255263050001] [execute-service] [system] [2024/01/31 10:12:07.185] [INFO ] [main] [ChainedDynamicProperty$ChainLink] checkAndFlip:115  Flipping property: datasource-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647

[TID: 7d7f6eacc837482db27bc38bf3502961.1.17698255263050001] [execute-service] [system] [2024/01/31 10:12:07.188] [INFO ] [main] [DynamicServerListLoadBalancer] restOfInit:150  DynamicServerListLoadBalancer for client datasource-service initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=datasource-service,current list of Servers=[172.18.3.101:9527],Load balancer stats=Zone stats: {unknown=[Zone:unknown;	Instance count:1;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:172.18.3.101:9527;	Zone:UNKNOWN;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 08:00:00 CST 1970;	First connection made: Thu Jan 01 08:00:00 CST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:com.alibaba.cloud.nacos.ribbon.NacosServerList@7e544223

[TID: N/A] [execute-service] [system] [2024/01/31 10:12:07.859] [INFO ] [PollingServerListUpdater-0] [ChainedDynamicProperty$ChainLink] checkAndFlip:115  Flipping property: datasource-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647

...

1 概述:DynamicServerListLoadBalancer 组件 ∈ Netflix Ribbon 客户端负载均衡器

  • 在日志中看到的 DynamicServerListLoadBalancerNetflix Ribbon 负载均衡器核心组件。

它出现在 Spring Cloud 环境中,主要负责动态管理服务实例列表,并决定将请求发送到哪一个具体的服务器节点。

核心作用:它在做什么?

在微服务架构中,服务实例(如你的 datasource-service)的 IP 和端口可能会因为扩容、缩容或重启而频繁变化。DynamicServerListLoadBalancer 的存在就是为了解决“我该去哪找服务”的问题:

  • 服务发现(Fetching):它会定期从注册中心(如 Eureka, Nacos 或 Consul)拉取最新的服务实例列表。
  • 动态更新(Updating):当实例上线或下线时,它会自动刷新内存中的列表,保证请求不会发往已挂掉的节点。
  • 负载均衡策略(Rule Execution):根据预设的规则(如轮询、随机、响应时间权重等),从可用列表中选出一个最佳的实例。
  • 状态监控(Stats Keeping):记录每个实例的活跃连接数、故障次数等,用于熔断或规避不可用的节点。

日志关键信息拆解

从你提供的这条日志中,我们可以读出非常具体的运行状态:

字段 含义 你的数据
client 当前负载均衡器服务的对象 datasource-service
current list of Servers 目前内存中缓存的可选后端地址 [172.18.3.101:9527] (目前只有一台)
Zone stats 区域状态(多机房容灾用) unknown (未配置具体机房分区)
Circuit breaker 熔断器状态 tripped count: 0 (目前健康,未触发熔断)
  • 工作流程示意图
    当你的服务启动并看到 restOfInit:150 行代码 时,意味着负载均衡器已经完成了初始化,并成功拿到了第一批可用的服务列表。

温馨提示:如果后续你在日志中发现 current list of Servers=[](列表为空),通常意味着注册中心掉线了,或者目标服务 datasource-service 全部挂掉了,这会导致典型的 500 错误。

DynamicServerListLoadBalancer 是 Netflix Ribbon 库中的核心类,也是 Spring Cloud 早期(基于 Netflix 堆栈)实现客户端负载均衡的灵魂组件。

我们可以通过它的组成结构工作机制以及核心配置三个维度进行深度拆解。

核心组件架构

DynamicServerListLoadBalancer 并不是孤立工作的,它像一个“指挥官”,协调着以下四个关键组件:

  1. ServerList (列表获取)
  • 作用:定义如何获取服务器原始列表。
  • 实现:通常与注册中心(Eureka, Nacos, Consul)集成。例如 DiscoveryEnabledNIWSServerList 会定时向注册中心查询 datasource-service 的所有实例。
  1. ServerListFilter (列表过滤)
  • 作用:在获取原始列表后,根据特定规则(如区域亲和性 Zone Affinity)剔除不符合条件的实例。
  1. ServerListUpdater (更新策略)
  • 作用:控制何时去刷新列表。默认是 PollingServerListUpdater(定时任务刷新,默认 30 秒一次)。
  1. IRule (负载均衡算法)
  • 作用:在过滤后的最终列表中,决定选择哪一个实例。常见的有 RoundRobinRule(轮询)或 WeightedResponseTimeRule(响应时间权重)。

工作全流程解析

从你日志中的 restOfInit 方法开始,它的生命周期如下:

1. 初始化 (Initialization)

当程序第一次调用目标服务时,restOfInit 被触发。它会立即启动一个定时任务(ServerListUpdater),并进行第一次同步拉取,确保本地内存中有服务器列表。

2. 动态刷新 (The "Dynamic" Part)

它之所以是“动态”的,是因为它持有一个后台线程。

  • 拉取:每隔固定时间,通过 ServerList 实现类去远程抓取。
  • 对比与覆盖:对比本地缓存。如果发现 101 机器下线了,或者新增了 102 机器,它会更新内存中的 allServerList

3. 状态度量 (LoadBalancerStats)

正如你日志中看到的 Zone stats。它会持续监控每个 Server 的运行情况:

  • 并发量:当前有多少请求发往该机器。
  • 错误率:最近是否频繁超时。
  • 熔断状态:如果某台机器连续失败,它会被标记为“电路跳闸(Circuit Breaker Tripped)”,在一段时间内不再给它分发流量。

关键参数与配置

在 Spring 环境中,你可以通过配置文件(YAML/Properties)调整它的行为。以下是常见的优化方向:

配置项 (以 service-id 为前缀) 说明 默认值
ribbon.ServerListRefreshInterval 刷新服务列表的时间间隔 30000ms (30秒)
ribbon.NIWSServerListClassName 指定获取服务器列表的具体类 自动适配(如 Eureka 实现)
ribbon.NFLoadBalancerRuleClassName 指定负载均衡策略(如改为随机) RoundRobinRule

为什么在日志中看到它?

通常看到 DynamicServerListLoadBalancer 的 INFO 日志有三种情况:

  1. 服务启动/首次调用:你目前属于这种情况(restOfInit),表示 LB 已经准备就绪。
  2. 服务扩缩容:当它检测到 current list of Servers 的数量发生变化时,会打印更新日志。
  3. 排查故障:如果你发现请求报错,通过这个组件的日志可以一眼看出:LB 到底有没有拿到后端地址? 如果 list of Servers=[],那么问题就在服务发现层,而不是业务代码。

进阶思考

  • 随着 Spring Cloud 的演进,Netflix Ribbon 已进入【维护模式】。在最新的 Spring Cloud 中,它正逐渐被 Spring Cloud LoadBalancer 取代(其原理类似,但摒弃了 Netflix 的复杂架构,更加轻量化)。

X 参考文献

posted @ 2026-01-31 10:07  千千寰宇  阅读(1)  评论(0)    收藏  举报