什么是负载均衡

负载均衡(load balancing)是一种电子计算机技术,用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到优化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。 使用带有负载均衡的多个服务器组件,取代单一的组件,可以通过冗余提高可靠性。负载均衡服务通常是由专用软件和硬件来完成。主要作用是将大量作业合理地分摊到多个操作单元上进行执行,用于解决互联网架构中的高并发和高可用的问题。

简单来说,就是将所有请求先集中在一起,然后再根据特定的算法将这些请求分配出去,使各个服务器的效率都能最大化。

常见的负载均衡算法有:

  • 随机法(Random)
  • 加权随机法(Weight Random)
  • 轮询法(Round Robin)
  • 加权轮询法(Weight Round Robin)
  • 平滑加权轮询法(Smooth Weight Round Robin)
  • 地址哈希法(Hash)
  • 最小连接数法(Least Connections)

Ribbon 简介

Ribbon 是一个由 Netflix 创建并开源(目前已闭源)的客户端负载均衡器。


使用 Ribbon

导入 Ribbon

由于 Nacos 注册与发现中心已默认集成了 Ribbon,因此我们不必再手动导入。

image-20220820104543533


Ribbon 负载均衡策略

各负载均衡策略的关系继承图如下:

image-20220820105620540

主要提供了如下几种负载均衡策略

名称 策略解释
RoundRobinRule 轮询策略
WeightedResponseTimeRule 服务实例的平均响应时间越短则权重越大,那么服务实例被选中执行的概率也就越大
RandomRule 随机策略
RetryRule 在轮询的基础上进行重试(超过重试时间服务实例仍无效则进行轮询)
NacosRule 根据Nacos设置的权重进行挑选
ClientConfigEnabledRoundRobinRule 和RoundRobinRule一致,轮询策略
BestAvailableRule 过滤掉失效的服务实例然后找出并发请求最小的服务实例进行使用
AvailabilityFilteringRule 先过滤掉故障实例然后选择并发较小的服务实例
ZoneAvoidanceRule 默认负载均衡策略。根据判断server所在区域的性能和server的可用性来过滤服务实例。过滤成功后,使用轮询策略从过滤结果中选择服务实例

使用 Ribbon 默认负载均衡策略

只需要在 RestTemplate Bean类上添加一个 @LoadBalanced 注解即可使用 Ribbon 的默认负载均衡策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.pushihao.orderRibbon.config;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestConfig {

@LoadBalanced
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}

修改 Ribbon 负载均衡策略

  1. 通过配置类进行修改

注意:配置类不能放在 @SpringBootApplication 注解的 @ComponentScan 能扫描到的地方

image-20220820120034423

RibbonRandomRuleConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.pushihao.ribbon;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RibbonRandomRuleConfig {

//注意:方法名一定叫 iRule
@Bean
public IRule iRule() {
return new RandomRule();
}
}

RestConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.pushihao.orderRibbon.config;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import com.pushihao.ribbon.RibbonRandomRuleConfig;

@Configuration
@RibbonClients(value = {
@RibbonClient(name = "stock-service", configuration = RibbonRandomRuleConfig.class)
})
public class RestConfig {

@LoadBalanced
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}

  1. 通过配置文件进行修改

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
server:
port: 9002
spring:
cloud:
nacos:
server-addr: 127.0.0.1:8848
application:
name: order-service

# 第一行是服务名,负载均衡策略配置的值为类的全路径
stock-service:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule

自定义负载均衡策略

通过实现 IRule 接口或继承 AbstractLoadBalancerRule 抽象类可以自定义负载均衡策略,主要的逻辑代码在 choose 方法中。

比如我要自己实现一个随机策略的负载均衡器

CustomRuleConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.pushihao.ribbon;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class CustomRuleConfig extends AbstractLoadBalancerRule {
@Override
public Server choose(Object key) {
//获得当前请求的服务的实例
ILoadBalancer loadBalancer = this.getLoadBalancer();
List<Server> reachableServers = loadBalancer.getReachableServers();

int randomNum = ThreadLocalRandom.current().nextInt(reachableServers.size());

Server server = reachableServers.get(randomNum);
if (server.isAlive()) {
return server;
} else {
return null;
}
}

@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) { }
}

然后通过配置类或配置文件修改使用自己的负载均衡策略,例如

application.yml

1
2
3
stock-service:
ribbon:
NFLoadBalancerRuleClassName: com.pushihao.ribbon.CustomRuleConfig

配置 Ribbon 负载均衡预加载

Ribbon 的负载均衡器默认是懒加载,也就是当第一次访问时才会加载,那么就会导致第一次访问时间较长甚至出现网络连接超时的情况。我们可以通过开启预加载(启动服务时就加载)来解决这个问题。

application.yml

1
2
3
4
5
6
#预加载配置,默认为懒加载
ribbon:
eager-load:
enabled: true
# 需要开启预加载的服务名
clients: stock-service1,stock-service2