微服务-负载均衡
目前主流的负载方案分为以下两种:
集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的(比如 F5),也有软件的(比如 Nginx)。
客户端根据自己的请求情况做负载均衡,Ribbon 就属于客户端自己做负载均衡。
Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端的负载均衡工具,Ribbon 客户端组件提供一系列的完善的配置,如超时,重试等。
通过 LoadBalancer 获取到服务提供的所有机器实例,Ribbon 会自动基于某种规则(轮询,随机)去调用这些服务。Ribbon 也可以实现我们自己的负载均衡算法。
目前客户端的负载均衡有两套机制
Netflix Ribbon:闭源停更,但是负载均衡的机制较完善
Spring Cloud loadbalancer:支持的负载均衡策略比较少,它开源但是更像一套标准
# 客户端的负载均衡
例如 spring cloud 中的 ribbon,客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问,这是客户端负载均衡;即在客户端就进行负载均衡算法分配。
# 服务端的负载均衡
例如 Nginx,通过 Nginx 进行负载均衡,先发送请求,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配。
# 常见负载均衡算法(策略、方式)
随机,通过随机选择服务进行执行,一般这种方式使用较少
轮询,负载均衡默认实现方式,请求来之后排队处理
加权轮询,通过对服务器性能的分型,给高配置,低负载的服务器分配更高的权重,均衡各个服务器的压力;
地址 Hash,通过客户端请求的地址的 HASH 值取模映射进行服务器调度。hash(ip)%节点数量
最小链接数,即使请求均衡了,压力不一定会均衡,最小连接数法就是根据服务器的情况,比如请求积压数等参数,将请求分配到当前压力最小的服务器上。最小活跃数
# Nacos 使用 Ribbon
当前版本(2021.0.1.0)中 nacos-discovery 的依赖已经将 ribbon 移除掉了
遇事不决参考官方案例:https://github.com/nacos-group/nacos-examples
# LoadBalancer
Spring Cloud 提供了自己的客户端负载均衡器抽象和实现。
在 SpringBoot 项目中一般会存在两种配置方式(JavaConfig、application)
# 切换负载均衡策略
public class CustomLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
ObjectProvider<ServiceInstanceListSupplier> listSuppliers = loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class);
return new RandomLoadBalancer(listSuppliers,name);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
需要注意的是该类不要添加注解@Configuration
开启对应服务的负载均衡策略
/*
服务调用者
*/
@SpringBootApplication
@EnableDiscoveryClient
//负载均衡策略
@LoadBalancerClients(value = {
@LoadBalancerClient(configuration = {CustomLoadBalancerConfiguration.class})
})
public class UserApplication{
public static void main( String[] args ){
SpringApplication.run(UserApplication.class, args);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 自定义负载均衡器
编写自定义负载均衡器代码
public class MyLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private ObjectProvider<ServiceInstanceListSupplier> listSuppliers;
public MyLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> listSuppliers, String name) {
this.listSuppliers = listSuppliers;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = listSuppliers.getIfAvailable(new Supplier<ServiceInstanceListSupplier>() {
@Override
public NoopServiceInstanceListSupplier get() {
return new NoopServiceInstanceListSupplier();
}
});
//获取服务
Flux<List<ServiceInstance>> listFlux = supplier.get(request);
Mono<List<ServiceInstance>> next = listFlux.next();
//获取服务对应列表
Mono<Response<ServiceInstance>> mono = next.map(new Function<List<ServiceInstance>, Response<ServiceInstance>>() {
@Override
public Response<ServiceInstance> apply(@Nullable List<ServiceInstance> serviceInstances) {
ServiceInstance instance = serviceInstances.get(0);
System.out.println(instance.getHost() + "," + instance.getPort());
//返回对应的实例对象
return new DefaultResponse(instance);
}
});
return mono;
}
}
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
32
33
34
35
36
37
38
39
40
41
更换负载均衡器
public class CustomLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
ObjectProvider<ServiceInstanceListSupplier> listSuppliers = loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class);
return new MyLoadBalancer(listSuppliers,name);
}
}
2
3
4
5
6
7
8
9
10
11
12
配置负载均衡
@SpringBootApplication
@EnableDiscoveryClient
//负载均衡策略
@LoadBalancerClients(value = {
@LoadBalancerClient(configuration = {CustomLoadBalancerConfiguration.class})
})
public class UserApplication{
public static void main( String[] args ){
SpringApplication.run(UserApplication.class, args);
}
}
2
3
4
5
6
7
8
9
10
11
12