在Eureka中添加了一个@LoadBalanced注解,即可实现负载均衡功能,那原理是什么呢?负载均衡又是什么呢?
1.什么是负载均衡?
负载均衡简单来说就是让客户端的请求合理均匀的分发到多台目标服务器中,将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性的高可用网络基础框架。Ribbon就是一个典型的客户端负载均衡器,Ribbon会获取服务的所有地址,根据内部的负载均衡算法,获取本次请求的有效地址。
什么是Ribbon?
Ribbon是Netflixfa发布的一个负载均衡器,有助于控制HTTP和TCP客户端行为。在SpringCloud中,Eureka一般配合Ribbon进行使用,Ribbon提供了客户端负载均衡的功能,Ribbon利用从Eureka中读取到的服务信息,在调用服务节点提供的服务时,会合理的进行负载。在SpringCloud中可以将注册中心和Ribbon配合使用,Ribbon自动的从注册中心获取服务提供者的列表信息,并基于内置的负载均衡算法请求服务。
当有多个服务提供者时,Ribbon可以根据负载均衡的算法自动的选择需要调用的服务地址
2.负载均衡原理
SpringCloud底层其实就是利用一个名叫Ribbon的组件,来实现负载均衡功能的,服务消费者会向Ribbon负载均衡器发起请求,然后会被Eureka进行拉取,最后将可用的服务器地址发送给负载均衡器,负载均衡器会通过轮询的方式发送到服务提供者(负载均衡器默认是轮询机制--例如:有A和B两个服务,A一个B一个)
3.源码追踪
那么为什么只需要根据service名称就可用获取到了服务实例的IP和端口,它就是LoadBalancerIntercepor,这个类会在对RestTemplate的请求进行拦截,然后从Eureka根据服务的id获取服务列表,然后利用负载均衡算法得到真实的服务地址信息,替换服务id。
1.LoadBalancerIntercepor
从图上可用看到这里的intercept方法拦截了用户的HttpRequest请求,通过request.getURI()获取 请求路径,再通过getHost()获取uri路径的主机名,就是服务id,this.loadBalancer.execute()处理服务id和用户请求。
2.LoadBalancerClient
继续看execute方法:
通过getLoadBalancer(serviceId)根据服务的id获取ILoadBalancer,而LoadBalancer会拿着服务id去Eureka中获取服务列表并保存起来,然后通过getServer(loadBalance)利用内置的负载均衡算法,从服务列表中选择一个服务。重新再走一遍就会发现是另一个服务,这就实现了负载均衡。
3.负载均衡策略IRule
在启动类中注入一个Bean
@Bean
public IRule randomRule(){
return new RandomRule();
}
总结:SpringCloudRibbon的底层就是采用了一个拦截器,拦截了RestTemplate发出的请求,对地址做出了修改。
基本的流程如下:
- 拦截我们的RestTemplate请求
- RibbonLoadBalancerClient会从请求url中获取服务名称
- DynamicServerListLoadBalancer根据服务提供者到eureka拉取服务列表
- eureka返回列表
- IRule利用内置负载均衡规则,从列表中选择一个
- RibbonLoadBalancerClient修改请求地址,发送真实请求
4.负载均衡策略
负载均衡的规则都定义在IRule接口中,而IRule有很多不同的实现类:
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的<clientName>.<clientConfigNameSpace>.ActiveConnectionsLimit属性进行配置。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 |
BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
默认的实现就是ZoneAvoidanceRule是一种轮询方案
自定义负载均衡策略
通过定义IRue实现可以修改负载均衡规则,有两种方式:
1.代码方式:在启动类定义一个新的IRule:
@Bean
public IRule randomRule(){
return new RandomRule();
}
2.配置文件方式:在application.yml文件中,添加新的配置也可以修改规则:
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
注意:一般用默认的负载均衡规则,不做修改。
5.饥饿加载
Ribbon默认是采用懒加载,即第一次访问时才会创建LoadBalanceClient,请求时间会很长,而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,然后通过下面配置开启饥饿加载:
ribbon:
eager-load:
enabled: true
clients: userservice