负载均衡(Load Balance)
在系统并发量高,单个服务器处理能力能力达到最大的情况下,一般会用多台服务器组成集群系统提高整体的处理性能。
在多台服务器组成集群的系统中,我们一般要统一流量入口,通过流量调度,按照某一均衡算法,将用户大量的请求流量均衡地分发到集群中不同的服务器上。
将用户大量的请求按照一定的均衡算法平摊分配到多个服务器上运行,就是负载均衡。
使用服务器集群和负载均衡可以给我们带来的几个好处:
- 扩展服务器带宽
- 增强数据处理能力
- 增加吞吐量
- 提高网络的可用性和灵活性
负载均衡器(Ribbon)
Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡器,当我们将 Ribbon 和 Eureka 一起使用时,Ribbon 会从 Eureka Server(服务注册中心)中获取服务端列表,然后通过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的。
Ribbon负载均衡策略
Spring Cloud Ribbon 提供了一个 IRule 接口,该接口主要用来定义负载均衡策略,它有 7 个默认实现类,每一个实现类都是一种负载均衡策略
实现类 | 负载均衡策略 |
---|---|
RoundRobinRule | 按照线性轮询策略,即按照一定的顺序依次选取服务实例 |
RandomRule | 随机选取一个服务实例 |
RetryRule | 按照 RoundRobinRule(轮询)的策略来获取服务,如果获取的服务实例为 null 或已经失效,则在指定的时间之内不断地进行重试(重试时获取服务的策略还是 RoundRobinRule 中定义的策略),如果超过指定时间依然没获取到服务实例则返回 null 。 |
WeightedResponseTimeRule | WeightedResponseTimeRule 是 RoundRobinRule 的一个子类,它对 RoundRobinRule 的功能进行了扩展。 根据平均响应时间,来计算所有服务实例的权重,响应时间越短的服务实例权重越高,被选中的概率越大。刚启动时,如果统计信息不足,则使用线性轮询策略,等信息足够时,再切换到 WeightedResponseTimeRule。 |
BestAvailableRule | 继承自 ClientConfigEnabledRoundRobinRule。先过滤点故障或失效的服务实例,然后再选择并发量最小的服务实例。 |
AvailabilityFilteringRule | 先过滤掉故障或失效的服务实例,然后再选择并发量较小的服务实例。 |
ZoneAvoidanceRule | 默认的负载均衡策略,综合判断服务所在区域(zone)的性能和服务(server)的可用性,来选择服务实例。在没有区域的环境下,该策略与轮询(RandomRule)策略类似。 |
Ribbon负载均衡实例
我们要准备一个Eurek注册中心,三个服务提供者(服务名一样,端口不同,提供接口也一样),服务消费者(开启负载均衡),以及一个测试类(用来观察消费者具体调用的服务提供者的实例是哪个)。
我们前面快速搭建sping cloud微服务实例的时候,已经创建好了一个注册中心(springcloud-eureka),一个服务提供者(springcloud-provider),一个服务消费者(springcloud-consumer)。
创建三个服务提供者
在springcloud-provider下添加两个配置文件application-8083.yml和application-8084.yml。
application-8083.yml和application.yml一样,只需要改变端口即可:
spring:
application:
name: provider #微服务名称,不用改变
server:
port: 8083 # 服务端口号,修改
eureka:
client:
# healthcheck:
# enabled: true #健康检测
service-url:
defaultZone: http://admin:admin@localhost:8763/eureka/ #注册中心地址 添加用户密码admin:admin@
application-8084.yml配置如下:
spring:
application:
name: provider #微服务名称,不用改变
server:
port: 8084 # 服务端口号
eureka:
client:
# healthcheck:
# enabled: true #健康检测
service-url:
defaultZone: http://admin:admin@localhost:8763/eureka/ #注册中心地址 添加用户密码admin:admin@
服务提供方法修改如下:
@RestController
@RequestMapping("/")
public class UserController {
@Value("${server.port}")
public String port;
/**
* 从配置文件中取到端口号,并返回。这样根据返回结果就知道具体调用的服务实例了。
*/
@RequestMapping("/queryuser")
public String queryUser(){
/*业务逻辑*/
return "服务提供者:"+port+",查到用户";
}
}
服务提供者三个服务需要指定配置文件启动。
服务消费者开启负载均衡
Ribbon 可以与 RestTemplate(Rest 模板)配合使用,实现微服务之间的调用。
具体调用服务provider的接口“/queryuser”
@Controller
@RequestMapping("/test")
public class TestController {
public static final String SERVERNAME = "provider"; //服务提供方名称
@Autowired
RestTemplate restTemplate;
// http://127.0.0.1:8082/test/get
@RequestMapping("/get")
@ResponseBody
public String test(){
// 服务提供方的名称 provider ,再加上服务提供方的接口名queryuser 就可以完成调用了
String result = restTemplate.getForObject("http://" + SERVERNAME + "/queryuser", String.class);
System.out.println(result);
return result;
}
}
开启负载均衡 @LoadBalanced
@Bean
@LoadBalanced // 负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
启动服务
依次启动注册中心,服务消费者,以及三个服务提供者。
服务提供者指定配置文件启动:
一共启动5个服务
负载均衡具体服务实例调用观察
写一个测试类,访问10次服务消费者,观察具体调用服务提供实例的情况:
public class Test {
public static void main(String[] args) {
// 服务消费者访问路径 消费者会调用provider服务的queryuser接口
String url = "http://127.0.0.1:8082/test/get";
for (int i = 1;i<=10;i++){
String s = HttpUtil.get(url);
System.out.println("第"+i+"次访问,结果:"+s);
}
}
}
运行结果如下:
默认使用ZoneAvoidanceRule策略进行负载均衡,由于在单机环境下,没有区域(zone),表现结果与轮询(RandomRule)策略类似。
切换负载均衡策略
我们只需要在服务消费者的配置类中,将 IRule 的其他实现类注入到容器中即可。
在springcloud-consumer的配置类 中添加以下代码,将负载均衡策略切换为 RandomRule(随机)
@Bean
public IRule myRule() {
// RandomRule 为随机策略
return new RandomRule();
}
重启springcloud-consumer。访问测试类,结果如下:
可以看出,我们已经将负载均衡策略切换为 RandomRule(随机)