Spring Cloud Alibaba - Nacos(二)负载均衡器



一、简介

在Spring Cloud Nacos 2021以后就没有在默认使用Ribbon作为负载均衡器了,而且在Cloud官网中也推荐使用LoadBnancer作为负载均衡器,他实现了轮询和随机两种方式(RandomLoadBalancer、RoundRobinLoadBalancer),如果引入了NacosDiscovery的话里面还可以使用NacosLoadBnancer的方式。所以接下来我们就来实现一下如何使用LoadBnlancer吧。

二、使用LoadBnlancer

因为本系列是SpringCloudAlibaba系列的一次学习记录,所以使用版本如下,也可以去官网看看最近的版本说明

Spring Cloud Alibaba VersionSentinel VersionNacos VersionRocketMQ VersionDubbo VersionSeata Version
2021.0.1.0*1.8.31.4.24.9.22.7.151.4.2
Spring Cloud Alibaba VersionSpring Cloud VersionSpring Boot Version
2021.0.1.0Spring Cloud 2021.0.12.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);
    }
}

--------------最后感谢大家的阅读,愿大家技术越来越流弊!--------------

在这里插入图片描述

--------------也希望大家给我点支持,谢谢各位大佬了!!!--------------

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值