一、简介
在Spring Cloud Nacos 2021以后就没有在默认使用Ribbon作为负载均衡器了,而且在Cloud官网中也推荐使用LoadBnancer作为负载均衡器,他实现了轮询和随机两种方式(RandomLoadBalancer、RoundRobinLoadBalancer),如果引入了NacosDiscovery的话里面还可以使用NacosLoadBnancer的方式。所以接下来我们就来实现一下如何使用LoadBnlancer吧。
二、使用LoadBnlancer
因为本系列是SpringCloudAlibaba系列的一次学习记录,所以使用版本如下,也可以去官网看看最近的版本说明
Spring Cloud Alibaba Version | Sentinel Version | Nacos Version | RocketMQ Version | Dubbo Version | Seata Version |
---|---|---|---|---|---|
2021.0.1.0* | 1.8.3 | 1.4.2 | 4.9.2 | 2.7.15 | 1.4.2 |
Spring Cloud Alibaba Version | Spring Cloud Version | Spring Boot Version |
---|---|---|
2021.0.1.0 | Spring Cloud 2021.0.1 | 2.6.3 |
在使用了这些版本后,就不需要像网上说的还需要去排除ribbon的包和在nacos上禁用ribbon。因为该版本本来就没有使用ribbon的依赖。
我们只需要在pom中引入如下依赖
<!--loadbalancer负载均衡器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
然后在注入的RestTemplateBean上添加@LoadBalanced注解
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
这时候使用restTemplate进行请求同一个服务的时候就会默认使用LoadBnlancer中的轮询策略(RoundRobinLoadBalancer)了
@GetMapping("/add")
public String add() {
log.info("添加商品!");
String reduce = restTemplate.getForObject("http://stock-service/stock/reduce", String.class);
return "添加商品成功." + reduce;
}
三、切换策略
LoadBnlancer除了默认的轮询策略外还提供了随机策略RandomLoadBalancer,如果你使用了nacos的注册发现功能的话,还可以使用spring-cloud-starter-alibaba-nacos-discovery包里的NacosLoadBalancer策略。
首先我们需要创建一个类用来构建RandomLoadBalancer,但是需要注意的是,这个类不能被加载到spring的上下文中,也就是说这个类要么不添加@Configuration之类的注解,要么不在springscan的扫描包之内。
public class LoadBalancerConfig {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
然后我们需要在启动类上添加注解进行配置
第一种直接使用 @LoadBalancerClients(defaultConfiguration = LoadBalancerConfig.class),意思就是所以的服务都使用我们创建的LoadBalancerConfig中策略,具体见下面的代码
第二种是使用@LoadBalancerClients对不同的服务配置不同的策略,具体见下面的代码
第三种是我们只有一个服务的时候直接使用@LoadBalancerClient配置一个就行。
@SpringBootApplication
// 第一种
// @LoadBalancerClients(defaultConfiguration = LoadBalancerConfig.class)
// 第二种
/** @LoadBalancerClients(value = {
@LoadBalancerClient(name = "stock-service", configuration = LoadBalancerConfig.class),
@LoadBalancerClient(name = "stock-service1", configuration = LoadBalancerConfig1.class)
})*/
// 第三种
@LoadBalancerClient(name = "stock-service", configuration = LoadBalancerConfig.class)
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
当然我们也可以配置NacosLoadBalancer策略。只是需要注入NacosDiscoveryProperties属性,然后对应的换一下对象就行。
public class LoadBalancerConfig {
// 注入当前服务的nacos的配置信息
@Resource
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Bean
ReactorLoadBalancer<ServiceInstance> nacosLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new NacosLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name, nacosDiscoveryProperties);
}
}
其实对于NacosLoadBalancer还有一个更加简单配置,如下只有设置为true就会使用NacosLoadBalancer策略,当然这种方式没有上面设置的优先级高。
spring:
application:
name: order-service
cloud:
loadbalancer:
nacos:
enabled: true
四、自定义策略
这个我们就可以模仿RandomLoadBalancer进行修改,直接负责出来完整的代码,修改getInstanceResponse这个方法就行了,然后安装之前将的,将他注入进去就行了。
package com.alibaba.order.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.*;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* @projectName springcloudalibaba-demo
* @title CustomBalancer
* @description
* @date 27/04/2022 10:37
*/
public class CustomBalancer implements ReactorServiceInstanceLoadBalancer {
private static final Log log = LogFactory.getLog(RandomLoadBalancer.class);
private final String serviceId;
private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
public CustomBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
}
@SuppressWarnings("rawtypes")
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next().map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
}
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
List<ServiceInstance> serviceInstances) {
Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + serviceId);
}
return new EmptyResponse();
}
// 这段代码就随机获取的核心 我们可以按照自己的规则来定制
int index = ThreadLocalRandom.current().nextInt(instances.size());
ServiceInstance instance = instances.get(index);
return new DefaultResponse(instance);
}
}
--------------最后感谢大家的阅读,愿大家技术越来越流弊!--------------
--------------也希望大家给我点支持,谢谢各位大佬了!!!--------------