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-servicecloud:nacos:discovery:server-addr: 127.0.0.1:8848# 不使用ribbon,使用loadbalancerloadbalancer: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*/@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}/*** 有负责均衡能力的RestTemplate* @return*/@Bean@LoadBalancedpublic 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 {@Resourceprivate RestTemplate restTemplate;@Resourceprivate 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-servicecloud:nacos:discovery:server-addr: 127.0.0.1:8848# 不使用ribbon,使用loadbalancerloadbalancer: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 {@BeanWebClient.Builder webClientBuilder() {return WebClient.builder();}@Bean@LoadBalancedWebClient.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 {@Resourceprivate WebClient.Builder webClientBuilder;@Resourceprivate 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);
}