SpringCloud Alibaba集成 Gateway(自定义负载均衡器)、Nacos(配置中心、注册中心)、loadbalancer

文章目录

POM依赖

pom 复制代码
 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.10</version>
        <relativePath/>
    </parent>
      <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <springcloud.version>3.1.6</springcloud.version>
        <springcloudalibaba.version>2021.0.4.0</springcloudalibaba.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
     <dependencies>
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>${springcloudalibaba.version}</version>
            <exclusions>
                <exclusion>
                    <!-- nacos-client2.0.4版本存在官方github上的#6999及#10385号Bug; nacos-client2.2.1版本存在当配置中心配置变化后客户端AsyncAppender进程数不断增加Bug -->
                    <groupId>com.alibaba.nacos</groupId>
                    <artifactId>nacos-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>${springcloudalibaba.version}</version>
            <exclusions>
                <exclusion>
                    <!-- nacos-client2.0.4版本存在官方github上的#6999及#10385号Bug; nacos-client2.2.1版本存在当配置中心配置变化后客户端AsyncAppender进程数不断增加Bug -->
                    <groupId>com.alibaba.nacos</groupId>
                    <artifactId>nacos-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
            <version>${springcloud.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>${springcloud.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
            <version>${springcloud.version}</version>
        </dependency>
         <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
                <version>2.2.9.RELEASE</version>
            </dependency>
         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>3.1.5</version>
        </dependency>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-webmvc</artifactId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
     </dependencies>

环境准备

nacos搭建Nacos standalone单机搭建部署

配置

配置文件

application.yml

yml 复制代码
server:
  port: 8082
spring:
  profiles:
    active: dev
  main:
    web-application-type: reactive
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: Mes
          uri: lb://Mes
          predicates:
            - Path=/mes/**
        - id: Test
          uri: lb://Test
          predicates:
            - Path=/test/**

bootstrap.properties

properties 复制代码
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.group=dev
spring.cloud.nacos.discovery.namespace=1e6d33e2-5f43-45ec-8c1b-9c883c2c71d9
spring.cloud.nacos.config.group=dev
spring.cloud.nacos.config.prefix=gateway
spring.cloud.nacos.config.server-addr=localhost:8848
spring.cloud.nacos.config.file-extension=properties
spring.cloud.nacos.config.namespace=1e6d33e2-5f43-45ec-8c1b-9c883c2c71d9
spring.profiles.active=dev
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedOriginPatterns=*
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedHeaders=*
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedMethods=*
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowCredentials=true

bootstrap-dev.properties

复制代码
# 用于配置中心测试
message.name=lisi

配置类

自定义Gateway负载均衡器,采用nacos所配置的权重进行负载均衡调用,随机权重算法

java 复制代码
import com.alibaba.cloud.nacos.balancer.NacosBalancer;
import com.alibaba.nacos.api.naming.pojo.Instance;
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.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class CustomRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private static final Log log = LogFactory.getLog(RoundRobinLoadBalancer.class);
    final AtomicInteger position;
    final String serviceId;
    ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

    /**
     * @param serviceInstanceListSupplierProvider a provider of
     *                                            {@link ServiceInstanceListSupplier} that will be used to get available instances
     * @param serviceId                           id of the service for which to choose an instance
     */
    public CustomRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));
    }

    /**
     * @param serviceInstanceListSupplierProvider a provider of
     *                                            {@link ServiceInstanceListSupplier} that will be used to get available instances
     * @param serviceId                           id of the service for which to choose an instance
     * @param seedPosition                        Round Robin element position marker
     */
    public CustomRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, int seedPosition) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        this.position = new AtomicInteger(seedPosition);
    }

    @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;
    }

    /**
     * 按nacos权重
     *
     * @return
     */

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> serviceInstances) {
        Map<String, List<ServiceInstance>> collect = serviceInstances.stream().collect(Collectors.groupingBy(g -> {
                        //nacos在2.0版本之后移除了对实例id查询
//            return g.getMetadata().get("nacos.instanceId");
            return g.getHost() +":"+ g.getPort();
        }));
        if (serviceInstances.isEmpty()) {
            if (log.isWarnEnabled()) {
                log.warn("No servers available for service: " + serviceId);
            }
            return new EmptyResponse();
        }
        List<Instance> instances = serviceInstances.stream().map(i -> {
            Instance instance = new Instance();
            instance.setInstanceId(i.getInstanceId());
            Map<String, String> metadata = i.getMetadata();
            instance.setInstanceId(metadata.get("nacos.instanceId"));
            instance.setWeight(new BigDecimal(metadata.get("nacos.weight")).doubleValue());
            instance.setClusterName(metadata.get("nacos.cluster"));
            instance.setEphemeral(Boolean.parseBoolean(metadata.get("nacos.ephemeral")));
            instance.setHealthy(Boolean.parseBoolean(metadata.get("nacos.healthy")));
            instance.setPort(i.getPort());
            instance.setIp(i.getHost());
            instance.setServiceName(i.getServiceId());
            instance.setMetadata(metadata);
            return instance;
        }).collect(Collectors.toList());
        //采用nacos所配置的权重进行负载均衡调用,随机权重算法
        Instance instance = NacosBalancer.getHostByRandomWeight2(instances);
//        // TODO: enforce order?
//        int pos = Math.abs(this.position.incrementAndGet());
//        ServiceInstance instance = instances.get(pos % instances.size());
        return new DefaultResponse(collect.get(instance.getIp()+":"+ instance.getPort()).stream().findFirst().get());
    }
}

负载均衡配置类,指定使用哪一个负载均衡器

java 复制代码
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration
public class RoundRobinLoadBalancerConfig {
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new CustomRoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

LoadBalancerClient,负载均衡调用客户端,指定负载均衡器配置类,LoadBalancerClient注解中value要对用配置文件中路由的id

java 复制代码
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@LoadBalancerClients({@LoadBalancerClient(value = "Mes", configuration = RoundRobinLoadBalancerConfig.class), @LoadBalancerClient(value = "Test", configuration = RoundRobinLoadBalancerConfig.class)})
@Configuration
public class RestTemplateConfig {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

全局过滤器

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;

@Slf4j
@Component
public class RouteRecordGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                URI proxyRequestUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        long start = System.currentTimeMillis();

        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            long end = System.currentTimeMillis();
            log.info("实际调用地址为:{},调用耗时为:{}ms", proxyRequestUri, (end - start));
        }));
    }

    @Override
    public int getOrder() {
        // 优先级设为最低,先让RouteToRequestUrlFilter先调用
        return Ordered.LOWEST_PRECEDENCE;
    }
}

案例展示

Nacos服务列表

服务权重配置

服务测试代码

负载效果

配置中心测试代码

本地配置项

配置中心配置

效果

配置中心注意

这三个配置的拼接等于配置中心的配置 Data ID一定要正确

properties 复制代码
spring.cloud.nacos.config.prefix=gateway
spring.cloud.nacos.config.file-extension=properties
spring.profiles.active=dev
相关推荐
瑶山11 小时前
Spring Cloud微服务搭建四、集成RocketMQ消息队列
java·spring cloud·微服务·rocketmq·dashboard
码字的字节12 小时前
Spring Cloud服务注册与发现(一):手把手搭建Eureka Server,详解高可用配置
spring·spring cloud·eureka
大厂资深架构师12 小时前
Spring Cloud Eureka在后端系统中的服务剔除策略
spring·spring cloud·ai·eureka
岁岁种桃花儿13 小时前
SpringCloud从入门到上天:Nacos做微服务注册中心(二)
java·spring cloud·微服务
SoleMotive.18 小时前
谢飞机爆笑面经:Java大厂3轮12问真题拆解(Redis穿透/Kafka分区/MCP Agent)
redis·spring cloud·kafka·java面试·mcp
MrSYJ20 小时前
Redis 做分布式 Session
后端·spring cloud·微服务
研究司马懿1 天前
【云原生】Gateway API高级功能
云原生·go·gateway·k8s·gateway api
瑶山1 天前
Spring Cloud微服务搭建五、集成负载均衡,远程调用,熔断降级
spring cloud·微服务·负载均衡·远程调用·熔断降级
主机哥哥1 天前
阿里云OpenClaw部署全攻略,五种方案助你快速部署!
服务器·阿里云·负载均衡
金牌归来发现妻女流落街头2 天前
【从SpringBoot到SpringCloud】
java·spring boot·spring cloud