- 前言
- 第一部分 基础应用开发
- 第 1 章 Spring Boot 入门
- 第 2 章 在 Spring Boot 中使用数据库
- 第 3 章 Spring Boot 界面设计
- 第 4 章 提高数据库访问性能
- 第 5 章 Spring Boot 安全设计
- 第二部分 分布式应用开发
- 第 6 章 Spring Boot SSO
- 第 7 章 使用分布式文件系统
- 第 8 章 云应用开发
- 第 9 章 构建高性能的服务平台
- 第三部分 核心技术源代码分析
- 第 10 章 Spring Boot 自动配置实现原理
- 第 11 章 Spring Boot 数据访问实现原理
- 第 12 章 微服务核心技术实现原理
- 附录 A 安装 Neo4j
- 附录 B 安装 MongoDB
- 附录 C 安装 Redis
- 附录 D 安装 RabbitMQ
- 结束语
12.3 负载均衡源代码剖析
当一个应用启用发现服务的功能之后,会默认启用 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 服务的访问。我们只是使用了默认的简单轮询负载均衡规则,即第一次调用将访问第一个服务实例,第二次调用将访问第二个服务实例,以此类推,当调用到服务列表的最后一个服务后再从头来过。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论