在之前,我们使用Eureka实现负载均衡是通过注解@LoadBalanced,对于这个注解里面是怎样实现的拉取和负载均衡等,都一概不知。
该注解的负载均衡是通过Ribbon这个组件实现的
一:负载均衡流程(原理)
当消费者通过地址发起请求去访问服务时:
- 因为地址不是申请的域名或IP,所以是无法实现直接在浏览器去访问这个地址的
- cloud中,该请求会去访问Ribbon(负载均衡组件),通过其去找到真实的地址
- Ribbon获取到请求地址后,获取到服务名称,将会去访问eureka-server拉取该服务名称
- eureka-server有的话,就会将该服务列表返回给Ribbon
- Ribbon获取到服务列表后,会通过负载均衡挑选具体的服务
- 基本逻辑如下图所示:
发出的请求从http://userservice/user/1,变成了http://localhost:8081?请看下一节源码分析
二:源码分析(代码执行流程)
在启动类中,我们通过给RestTemplatejia了一个注解,这个注解就是一个标记(标记当前这个RestTemplatejia发起的请求会被Ribbon去拦截和处理
)
LoadBalancerInterceptor
(这个类会根据service名称,获取到了服务实例的ip和端口
),这个类会在对RestTemplate的请求进行拦截,然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id。
2.1 LoadBalancerIntercepor
其实现了ClientHttpRequestInterceptor:这个接口会将客户端发起的http请求拦截
- 实现了intercept方法,拦截了用户的HttpRequest请求
- intercept方法里面通过
request.getURI()
:获取请求uri,本例中就是 http://user-service/user/8 - intercept方法里面通过
originalUri.getHost()
:获取uri路径的主机名,其实就是服务id,user-service
- intercept方法里面通过
this.loadBalancer.execute()
:处理服务id,和用户请求。this.loadBalancer
是LoadBalancerClient
类型
2.2 LoadBalancerClient
执行流程:
- getLoadBalancer(serviceId):根据服务id获取ILoadBalancer,而ILoadBalancer会根据服务id去eureka中获取服务列表并保存起来,到loadBalancer。
- getServer(loadBalancer):利用内置的负载均衡算法,从服务列表中选择一个。本例中,可以看到获取了8082端口的服务
- 放行后,再次访问并跟踪,发现获取的是8081:实现了负载均衡。在这里已经实现了拉取服务列表这一步
2.2 负载均衡策略IRule
在2.1中,获取服务使通过一个getServer
方法来做负载均衡的
进入下一步:还是在当前类中
继续下一步,其会在其他类ZoneAwareLoadBalancer这个类中,执行return super.chooseServer(key)
继续下一步,会进入另一个类中,进入源码,查看chooseServer方法,发现这么一段代码:
查看rule,
这里的rule默认值是一个RoundRobinRule
,看类的介绍;是轮询的意思。
三:流程总结
四:负载均衡策略
在该节里面,将详细的解释IRule这个接口的实现,及修改这些实现
4.1 负载均衡规则接口
默认的实现是ZoneAvoidanceRule,
Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每一个子接口都是一种规则:
4.2 负载均衡有的规则含义
不同规则的含义如下:
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的< clientName>.< clientConfigNameSpace>.ActiveConnectionsLimit属性进行配置。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 |
BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
4.3 调整负载均衡方案
注意,一般用默认的负载均衡规则,不做修改。
4.3.1 方法一:代码方式
在消费者的启动类Application类中,定义一个新的IRule:
如下所示:将会实现RandomRule(),配置过后,规则将会从轮询变为随机
@Bean
public IRule randomRule(){
return new RandomRule();
}
作用于全局的,一旦这样配置过后,不管是调用哪一个服务,都是随机
4.3.2 方法二:配置文件方式
在消费者的application.yml配置文件文件中,添加新的配置也可以修改规则:
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
针对某个微服务配置的
五:饥饿加载
Ribbon 默认是采用懒加载,
- 懒加载:即
第一次访问时才会去创建
LoadBalanceClient,还要去拉取,所以请求时间会很长。
饥饿加载则会在项目启动时创建
,降低第一次访问的耗时
通过下面配置开启饥饿加载:
- 只有一个的话:
ribbon: eager-load: enabled: true #开启饥饿加载 clients: userservice #指定对哪一个服务(userservice)饥饿加载
- 如果有多个的话
ribbon: eager-load: enabled: true #开启饥饿加载 clients: #指定对哪一个服务(userservice)饥饿加载 - userservice - 其他service