- 前言
- 第一部分 基础应用开发
- 第 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 服务的访问。我们只是使用了默认的简单轮询负载均衡规则,即第一次调用将访问第一个服务实例,第二次调用将访问第二个服务实例,以此类推,当调用到服务列表的最后一个服务后再从头来过。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论