Ribbon 负载均衡
- 一、负载均衡原理(debug 源码)
- (1) 基本介绍
- (2) 打断点
- ① LoadBalancerInterceptor.java - intercept()
- ② RibbonLoadBalancerClient.java - execute()
- ③ RibbonLoadBalancerClient.java - execute()
- ④ RibbonLoadBalancerClient.java - getServer()
- ⑤ ZoneAwareLoadBalancer.java - chooseServer()
- ⑥ BaseLoadBalancer.java - chooseServer()
- ⑦ RibbonLoadBalancerClient.java - execute()
- (3) 流程
- 二、负载均衡策略
- (1) 负载均衡策略
- (2) 调整负载均衡策略
- ① 注入(@Bean)自己需要的负载均衡策略(IRule)
- ② yaml 文件指定对某个指定微服务发送请求的使用采用指定的负载均衡策略
- (3) 饥饿加载
📝 【上篇文章】
🔖 用 Eureka 做注册中心
🔖 user-service 的多个实例向 Eureka 注册中心注册自己的服务信息
🔖 order-service 通过服务名(如 userservice)获取 Eureka 注册中心里面指定服务名(如 userservice)的服务列表
🔖 通过一个服务名(如 userservice)可以获取到多个服务地址信息
❓ 究竟通过哪个地址信息发送请求呢 ❓
🔖 这通过【负载均衡】来决定
一、负载均衡原理(debug 源码)
(1) 基本介绍
📖
@LoadBalanced
注解表示:将来 RestTemplate 发起的请求要被 ribbon 拦截
📖 这个拦截操作是通过
LoadBalancerInterceptor
完成的
📖 它是 SpringCloud 中的拦截器org.springframework.cloud.client.loadbalancer
📖LoadBalancerInterceptor
是ClientHttpRequestInterceptor
接口的实现类
(2) 打断点
① LoadBalancerInterceptor.java - intercept()
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
// 获取请求地址
final URI originalUri = request.getURI();
// 获取主机名(微服务的服务名)
String serviceName = originalUri.getHost();
// 判断服务名是否为空
Assert.state(serviceName != null,
"Request URI does not contain a valid hostname: " + originalUri);
// this.loadBalancer: RibbonLoadBalancerClient
// 进入 this.loadBalancer.execute 方法
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
② RibbonLoadBalancerClient.java - execute()
public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
throws IOException {
// serviceId: 服务名
return execute(serviceId, request, null);
}
③ RibbonLoadBalancerClient.java - execute()
🔋 调用 RibbonLoadBalancerClient 重载的另一个
execute()
方法
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
// 通过服务名获取负载均衡器
// loadBalancer 的 allServerList 中包含服务地址信息
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
// 负载均衡, 获取某个服务地址信息
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
④ RibbonLoadBalancerClient.java - getServer()
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
if (loadBalancer == null) {
return null;
}
// Use 'default' on a null hint, or just pass it on?
return loadBalancer.chooseServer(hint != null ? hint : "default");
}
⑤ ZoneAwareLoadBalancer.java - chooseServer()
🔋 调用父类的
chooseServer()
⑥ BaseLoadBalancer.java - chooseServer()
public Server chooseServer(Object key) {
if (counter == null) {
counter = createCounter();
}
counter.increment();
// rule 默认是 ZoneAvoidanceRule 类型, 不为 null
if (rule == null) {
return null;
} else {
try {
// 根据 key 选择一个活着的的服务
return rule.choose(key);
} catch (Exception e) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e);
return null;
}
}
}
⑦ RibbonLoadBalancerClient.java - execute()
🔋 拿真实的服务地址替换服务名
(3) 流程
二、负载均衡策略
(1) 负载均衡策略
📝 Ribbon 的负载均衡策略是通过一个叫做
IRule
的接口来定义的,每个实现类是一种策略
(2) 调整负载均衡策略
📝 有两种方式指定
IRule
实现,进而修改负载均衡规则
① 注入(@Bean)自己需要的负载均衡策略(IRule)
全局:整个微服务
@Bean
public IRule iRule() {
return new RandomRule();
}
📝 整个微服务发送的请求都通过【随机】方式负载均衡
② yaml 文件指定对某个指定微服务发送请求的使用采用指定的负载均衡策略
局部:只对某个微服务有作用
userservice: # 该微服务向 userservice 发送的请求使用【随机】负载均衡
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
(3) 饥饿加载
📝 Ribbon 默认是采用懒加载 【第一次访问时才会创建LoadBalanceClient,请求时间会很长】
📝 饥饿加载会让 LoadBalanceClient 在项目启动时被创建,进而降低第一次访问的耗时
ribbon:
eager-load:
enabled: true # 开启饥饿加载
clients:
- userservice # 指定对哪些微服务进行饥饿加载
📖 根据 Bilibili 黑马程序员进行学习
📖 如有错误,请不吝赐教哦