一、负载均衡

Spring Cloud Ribbon 是一个基于 Http 和 TCP 的客户端负载均衡工具 ,Ribbon 主要解决集群服务中,多个服务高效率访问的问题。

负载均衡在系统架构中是一个非常重要,并且是不得不去实施的内容。因为负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。

客户端与服务端级别的负载均衡

  • 服务器端负载均衡:例如 Nginx,通过 Nginx 进行负载均衡过程如下:先发送请求给 nginx 服务器,然后通过负载均衡算法,在多个业务服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配。
  • 客户端负载均衡:客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问,即在客户端就进行负载均衡算法分配。

服务端负载均衡

负载均衡是微服务架构中必须使用的技术,通过负载均衡来实现系统的高可用、集群扩容等功能。负载均衡可通过硬件设备及软件来实现,硬件比如:F5、Array 等,软件比如:LVS、Nginx 等。

客户端负载均衡

客户端负载均衡与服务端负载均衡的区别在于客户端要维护一份服务列表,Ribbon 从 Eureka Server 获取服务列表,Ribbon 根据负载均衡算法直接请求到具体的微服务,中间省去了负载均衡服务。

二、Ribbon

Ribbon 是 Netflix 发布的负载均衡器,有助于控制 HTTP 客户端行为。为 Ribbon 配置服务提供者地址列表后,Ribbon 就可基于负载均衡算法,自动帮助服务消费者请求。

Ribbon 默认提供的负载均衡算法

  • 轮询(默认)
  • 随机算法
  • 重试算法
  • 加权法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#指定eureka服务地址
eureka:
client:
service-url:
# EurekaServer的地址
defaultZone: http://localhost:7001/eureka
# 修改服务地址轮询策略,默认是轮询,配置之后变随机
user-provider:
ribbon:
#轮询
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
#随机算法
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#重试算法,该算法先按照轮询的策略获取服务,如果获取服务失败则在指定的时间内会进行重试,获取可用的服务
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule
#加权法,会根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越大。刚启动时如果同统计信息不足,则使用轮询的策略,等统计信息足够会切换到自身规则。
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.ZoneAvoidanceRule

1、负载均衡源码跟踪探究

为什么只输入了Service名称就可以访问了呢?不应该需要获取 ip 和端口吗?

负载均衡器动态的从服务注册中心中获取服务提供者的访问地址(host、port)

显然是有某个组件根据Service名称,获取了服务实例 ip 和端口。就是LoadBalancerInterceptor

这个类会对 RestTemplate 的请求进行拦截,然后从Eureka根据服务 id 获取服务列表,随后利用负载均衡算法得到真正服务地址信息,替换服务 id。

2、结论

  • Ribbon 的负载均衡算法应用在客户端(Http 请求),只需要提供服务列表,就能帮助消费端自动访问服务端,并通过不同算法实现负载均衡。
  • Ribbon 的轮询、随机算法配置:在application.yml中配置 {服务名称}.ribbon.NFLoadBalancerRuleClassName
  • 负载均衡的切换:在LoadBalancerInterceptor中获取服务的名字,通过调用RibbonLoadBalancerClientexecute方法,并获取ILoadBalancer负载均衡器,然后根据ILoadBalancer负载均衡器查询出要使用的节点,再获取节点的信息,并实现调用。

三、实现

Eureka 已经集成 Ribbon,所以无需引入依赖,要想使用 Ribbon,直接在 RestTemplate 的配置方法上添加@LoadBalanced注解即可

1、修改user-consumercom.atguigu.UserConsumerApplication启动类,在restTemplate()方法上添加@LoadBalanced注解

1
2
3
4
5
6
@Bean
// 开启负载均衡
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}

2、采用服务名访问配置

修改user-consumerUserController的调用方式,不再手动获取 ip 和端口,而是直接通过服务名称调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@RestController
@RequestMapping(value = "/consumer")
public class UserController {

@Autowired
private RestTemplate restTemplate;

@Autowired
private DiscoveryClient discoveryClient;

/****
* 在user-consumer服务中通过RestTemplate调用user-provider服务
* @param id
* @return
*/
@GetMapping(value = "/{id}")
public User queryById(@PathVariable(value = "id") Integer id) {

String url = "http://user-provider/user/find/" + id;
return restTemplate.getForObject(url, User.class);
}
}

四、负载均衡原理

在 RestTemplate 中只需要给RestTemplate实例上添加一个@LoadBalanced注解,estTemplate 就会自动具备负载均衡功能。

这个功能的实现大致分三步:

  1. 从 Eureka Client 本地缓存的服务注册信息中,选择一个可调用的服务
  2. 根据 1 中选择的服务,重构请求 URL 地址
  3. 将 1、2 步的功能嵌入到 RestTemplate 中