返回介绍

12.3 负载均衡源代码剖析

发布于 2025-04-26 13:26:37 字数 5409 浏览 0 评论 0 收藏

当一个应用启用发现服务的功能之后,会默认启用 Ribbon 的负载均衡服务。Ribbon 通过发现服务获取在线的客户端,为具有多个实例的客户端建立起负载均衡实例列表,然后通过一定的负载均衡算法,实现负载均衡的管理机制。

如代码清单 12-9 所示,Ribbon 默认结合使用 Eureka 发现服务,启用负载均衡管理机制。当没有配置“ribbon.eureka.enabled”参数时,它默认被设定为 true。

代码清单 12-9 读取启用负载均衡配置的源代码

package org.springframework.cloud.netflix.ribbon.eureka;
......
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass({DiscoveryEnabledNIWSServerList.class})
@ConditionalOnBean({SpringClientFactory.class})
@ConditionalOnProperty(
    value = {"ribbon.eureka.enabled"},
    matchIfMissing = true
)
@AutoConfigureAfter({RibbonAutoConfiguration.class})
@RibbonClients(
    defaultConfiguration = {EurekaRibbonClientConfiguration.class}
)
public class RibbonEurekaAutoConfiguration {
    public RibbonEurekaAutoConfiguration() {
    }
}

看看负载均衡服务是如何进行初始化的,就更清楚它的实现原理了。代码清单 12-10 是 BaseLoadBalancer 的部分源代码。这里,程序加载了一些初始配置,如可用的负载均衡服务实例列表、监控计数和服务状态监听等,其中一个重要的设置,即设定默认的负载均衡规则 RoundRobinRule,这是一个使用简单轮询算法的负载均衡规则。一个负载均衡服务的实现,就是通过一定的负载均衡算法,从可用的服务实例列表中,为请求者提供一个可用的服务。

代码清单 12-10 BaseLoadBalancer 源代码片段

package com.netflix.loadbalancer;
......
public class BaseLoadBalancer extends AbstractLoadBalancer implements Prime
ConnectionListener, IClientConfigAware {
    private static Logger logger = LoggerFactory.getLogger(BaseLoadBalancer.
class);
    private static final IRule DEFAULT_RULE = new RoundRobinRule();
    private static final String DEFAULT_NAME = "default";
    private static final String PREFIX = "LoadBalancer_";
    protected IRule rule;
    protected IPing ping;
    @Monitor(
        name = "LoadBalancer_AllServerList",
        type = DataSourceType.INFORMATIONAL
    )
    protected volatile List<Server> allServerList;
    @Monitor(
        name = "LoadBalancer_UpServerList",
        type = DataSourceType.INFORMATIONAL
    )
    public BaseLoadBalancer() {
        this.rule = DEFAULT_RULE;
        this.ping = null;
        this.allServerList = Collections.synchronizedList(new ArrayList());
        this.upServerList = Collections.synchronizedList(new ArrayList());
        this.allServerLock = new ReentrantReadWriteLock();
        this.upServerLock = new ReentrantReadWriteLock();
        this.name = "default";
        this.lbTimer = null;
        this.pingIntervalSeconds = 10;
        this.maxTotalPingTimeSeconds = 5;
        this.serverComparator = new ServerComparator();
        this.pingInProgress = new AtomicBoolean(false);
        this.counter = Monitors.newCounter("LoadBalancer_ChooseServer");
        this.enablePrimingConnections = false;
        this.changeListeners = new CopyOnWriteArrayList();
        this.serverStatusListeners = new CopyOnWriteArrayList();
        this.name = "default";
        this.ping = null;
        this.setRule(DEFAULT_RULE);
        this.setupPingTask();
        this.lbStats = new LoadBalancerStats("default");
    }
    ......
}

再来看看 RoundRobinRule 的实现代码,如代码清单 12-11 所示。从中可以看出,它使用一个循环,从可用的服务列表中,按顺序选择一个可用的服务。其中,一个选择服务的请求不能超过 10 次无效的尝试,这仅仅是一个循环语句的安全设计而已,并不会影响一次选择查询。

从整体上来说,使用哪种负载均衡算法,对于整个负载均衡服务来说,影响并不是很大。除了 RoundRobinRule,Ribbon 还提供了其他一些负载均衡规则,如加权响应时间规则 WeightedResponseTimeRule、区域感知规则 ZoneAvoidanceRule、随机规则 RandomRule 等。

代码清单 12-11 RoundRobinRule 源代码片段

package com.netflix.loadbalancer;
......
public class RoundRobinRule extends AbstractLoadBalancerRule {
AtomicInteger nextIndexAI;
    private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);
    static final boolean availableOnly = false;
    public RoundRobinRule() {
        this.nextIndexAI = new AtomicInteger(0);
    }
......
    public Server choose(ILoadBalancer lb, Object key) {
        if(lb == null) {
            log.warn("no load balancer");
            return null;
        } else {
            Server server = null;
            boolean index = false;
            int count = 0;
            while(true) {
                if(server == null && count++ < 10) {
                    List upList = lb.getServerList(true);
                    List allList = lb.getServerList(false);
                    int upCount = upList.size();
                    int serverCount = allList.size();
                    if(upCount != 0 && serverCount != 0) {
                        int var10 = this.nextIndexAI.incrementAndGet() % server
Count;
                        server = (Server)allList.get(var10);
                        if(server == null) {
                            Thread.yield();
                        } else {
                            if(server.isAlive() && server.isReadyToServe()) {
                                return server;
                            }
                            server = null;
                        }
                        continue;
                    }
                    log.warn("No up servers available from load balancer: " + lb);
                    return null;
                }
                if(count >= 10) {
                    log.warn("No available alive servers after 10 tries from
 load balancer: " + lb);
                }
                return server;
            }
        }
    }
   ......
}

例如在第 9 章的演示中,当把其中的 data 服务配置为两个或两个以上的运行实例时,就会启动 Ribbon 的负载均衡机制,用来管理其他服务对 data 服务的访问。我们只是使用了默认的简单轮询负载均衡规则,即第一次调用将访问第一个服务实例,第二次调用将访问第二个服务实例,以此类推,当调用到服务列表的最后一个服务后再从头来过。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。