Spring Cloud LoadBalancer是Spring Cloud官方自己提供的客户端负载均衡器, 用来替代Ribbon。
Spring官方提供了两种客户端都可以使用loadbalancer:
- RestTemplate:Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。默认情况下,RestTemplate默认依赖jdk的HTTP连接工具。
- WebClient:从Spring WebFlux 5.0版本开始提供的一个非阻塞的基于响应式编程的进行Http请求的客户端工具。它的响应式编程的基于Reactor的。WebClient中提供了标准Http请求方式对应的get、post、put、delete等方法,可以用来发起相应的请求。
RestTemplate整合LoadBalancer
引入LoadBalancer的依赖
<!-- LoadBalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- 提供了RestTemplate支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos服务注册与发现 移除ribbon支持-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
注意:nacos-discovery中引入了ribbon,需要移除ribbon的包,如果不移除,也可以在yml中配置不使用ribbon。
默认情况下,如果同时拥有RibbonLoadBalancerClient和BlockingLoadBalancerClient,为了保持向后兼容性,将使用RibbonLoadBalancerClient。要覆盖它,可以设置spring.cloud.loadbalancer.ribbon.enabled属性为false。
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 不使用ribbon,使用loadbalancer
loadbalancer:
ribbon:
enabled: false
使用@LoadBalanced注解修饰RestTemplate,开启客户端负载均衡功能
package com.morris.user.config;
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 {
/**
* 默认的RestTemplate,不带负载均衡
* @return
*/
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
/**
* 有负责均衡能力的RestTemplate
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate2() {
return new RestTemplate();
}
}
RestTemplate的使用:
package com.morris.user.controller;
import com.morris.user.entity.Order;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("user")
public class RestTemplateController {
@Resource
private RestTemplate restTemplate;
@Resource
private RestTemplate restTemplate2;
@GetMapping("findOrderByUserId")
public List<Order> findOrderByUserId(Long userId) {
Order[] orders = restTemplate.getForObject("http://127.0.0.1:8020/order/findOrderByUserId?userId=", Order[].class, userId);
return Arrays.asList(orders);
}
@GetMapping("findOrderByUserId2")
public List<Order> findOrderByUserId2(Long userId) {
Order[] orders = restTemplate2.getForObject("http://order-service/order/findOrderByUserId?userId=", Order[].class, userId);
return Arrays.asList(orders);
}
}
WebClient整合LoadBalancer
引入依赖webflux,WebClient位于webflux内:
<!-- LoadBalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- webflux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- nacos服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
同样需要在配置文件中禁用ribbon:
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 不使用ribbon,使用loadbalancer
loadbalancer:
ribbon:
enabled: false
使用@LoadBalanced注解修饰WebClient.Builder,开启客户端负载均衡功能
package com.morris.user.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
public class WebClientConfig {
@Bean
WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
@Bean
@LoadBalanced
WebClient.Builder webClientBuilder2() {
return WebClient.builder();
}
}
WebClient负载均衡的使用:
package com.morris.user.controller;
import com.morris.user.entity.Order;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("user2")
public class WebClientController {
@Resource
private WebClient.Builder webClientBuilder;
@Resource
private WebClient.Builder webClientBuilder2;
@GetMapping("findOrderByUserId1")
public Mono<List<Order>> findOrderByUserId1(Long userId) {
return webClientBuilder.build().get().uri("http://127.0.0.1:8020/order/findOrderByUserId?userId=" + userId)
.retrieve().bodyToMono(Order[].class).map(t -> Arrays.asList(t));
}
@GetMapping("findOrderByUserId2")
public Mono<List<Order>> findOrderByUserId2(Long userId) {
return webClientBuilder2.build().get().uri("http://order-service/order/findOrderByUserId?userId=" + userId)
.retrieve().bodyToMono(Order[].class).map(t -> Arrays.asList(t));
}
}
原理:底层会使用ReactiveLoadBalancer
WebClient设置Filter实现负载均衡
与RestTemplate类似,@LoadBalanced注解的功能是通过SmartInitializingSingleton实现的。
SmartInitializingSingleton是在所有的bean都实例化完成之后才会调用的,所以在bean的实例化期间使用@LoadBalanced修饰的WebClient是不具备负载均衡作用的,比如在项目的启动过程中要使用调用某个微服务,此时WebClient是不具备负载均衡作用的。
可以通过手动为WebClient设置Filter实现负载均衡能力。
@Bean
WebClient.Builder webClientBuilder3(ReactorLoadBalancerExchangeFilterFunction lbFunction) {
return WebClient.builder().filter(lbFunction);
}