负载均衡LoadBalancer

nacos2.x已不支持ribbon,使用nacos2.x需要手动引入LoadBalancer

1 手写随机负载均衡算法

1
2
3
4
5
6
7
8
9
10
11
12
13
@Resource
private RestTemplate restTemplate;

//通过restTemplate访问随机一个url地址
@RequestMapping("/test")
public String loadbalance(){
List<ServiceInstance> instances = discoveryClient.getInstance("order");
List<String> uris = instances.stream().map(instance -> instance.getUri().toString() + "/order/query").collect(Collections.toList());
int i = ThreadLocalRandom.current().nextInt(uris.size());
String uri = uris.get(i);
System.out.printLn("访问地址为" + uri);
return this.restTemplate.getForObject(uri, String.calss);
}

2 LoadBalancerClient API

https://spring.io/guides/gs/spring-cloud-loadbalancer/

(1)引入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

(2)写代码

1
2
3
4
5
6
7
8
9
@Resource
private LoadBalancerClient loadBalancerClient;

@RequestMapping("/loadbalancerclient-api")
public String loadbalancerclientApi(){
ServiceInstance serviceInstance = loadBalancerClient.choose("order");
String url = serviceInstance.getUri().toString()+"/order/query";
return restTemplate.getForObject(url,String.class);
}

3 @LoadBalanced注解方式

(1)引入loadbalancer starter

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

(2)自定义一个@LoadBalanced注解的RestTemplate

1
2
3
4
5
@Bean
@LoadBalanced // 相当于为restTemplate整合了lb的能力
public RestTemplate restTemplateLoadBalancer(){
return new RestTemplate();
}

(3)写代码

1
2
3
4
5
6
7
@Resource
private RestTemplate restTemplateLoadBalancer;

@RequestMapping("/loadbalancerclient-annotation")
public String loadbalancerclientAnnotation(){
return this.restTemplateLoadBalancer.getForObject("http://order/order/query", String.class);
}

4 更改默认负载均衡方式

https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#switching-between-the-load-balancing-algorithms

默认的负载均衡为轮询

(1)定义配置类

1
2
3
4
5
6
7
public class CustomLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class), name);
}
}

(2)在启动类上通过@LoadBalancerClients注解使用自定义的配置类

1
2
3
@SpringBootApplication
@LoadBalancerClients(defaultConfiguration = CustomLoadBalancerConfiguration.class)
public class UserApplication {

5 核心源码分析

实际上,loadbalancer注解就是给RestTemplate添加了一个拦截器,调用过程简单来说,就是RestTemplate加上loadbalancer注解后,在创建请求之前添加了一个拦截器,在拦截器中通过服务名称调用LoadBalancer的choose方法,获取到真实的ip:port然后再拼接上具体的接口,替换原有uri,再通过RestTemplate进行远程调用。

5.1 LoadBalancerClient#choose

(1)LoadBalancerClient的自动装配

BlockingLoadBalancerClientAutoConfiguration

1
2
3
4
5
6
@Bean
@ConditionalOnBean(LoadBalancerClientFactory.class)
@ConditionalOnMissingBean
public LoadBalancerClient blockingLoadBalancerClient(LoadBalancerClientFactory loadBalancerClientFactory) {
return new BlockingLoadBalancerClient(loadBalancerClientFactory);
}

(2)LoadBalancerClient-choose源码分析

5.2 @LoadBalanced和RestTemplate

5.2.1 RestTamplate调用过程

5.2.2 RestTemplate使用@LoadBalanced

(1)向Spring IoC容器中注入RestTemplate的Bean

1
2
3
4
5
@Bean
@LoadBalanced // 相当于为RestTemplate整合了LoadBalancer的功能
public RestTemplate restTemplateLoadBalancer(){
return new RestTemplate();
}

(2)依赖注入

1
2
3
4
5
6
7
8
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false) // 获取到restTemplateLoadBalancer,然后给其添加拦截器
private List<RestTemplate> restTemplates = Collections.emptyList();

(3)创建拦截器

1
2
3
4
5
@Bean
public LoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}

(4)给指定的restTemplate添加拦截器属性

1
2
3
4
5
6
7
8
9
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}

(5)拥有了拦截器能力的RestTemplate


负载均衡LoadBalancer
http://www.zivjie.cn/2023/03/05/spring框架/springcloud/LoadBalancer/负载均衡LoadBalancer/
作者
Francis
发布于
2023年3月5日
许可协议