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

慈云数据 2024-05-13 技术支持 80 0

文章目录

  • 常见Gateway架构
  • Gateway核心点
  • 工作机制
  • POM依赖
  • 环境准备
  • 配置
    • 配置文件
    • 配置类
    • 案例展示

      在这里插入图片描述

      常见Gateway架构

      在这里插入图片描述

      Gateway核心点

      • 路由(route):路由是网关最基础的部分,路由信息由一个ID,一个目的URL、一组断言工厂和一 组Filter组成。如果断言为真,则说明请求URL和配置的路由匹配。

      • 断言(Predicate):Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是 Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配 来自http Request中的任何信息,比如请求头和参数等。

      • 过滤器(Filter):一个标准的Spring WebFilter,Spring Cloud Gateway中的Filter分为两种类型: Gateway Filter和Global Filter。过滤器Filter可以对请求和响应进行处理。

        工作机制

        在这里插入图片描述

        客户端向Spring Cloud网关发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。该处理程序运行通过特定于请求的筛选器链发送请求。筛选器由虚线分隔的原因是,筛选器可以在发送代理请求之前或之后执行逻辑。执行所有“前置”过滤器逻辑,然后发出代理请求。发出代理请求后,将执行“后”过滤器逻辑。

        POM依赖

         
                org.springframework.boot
                spring-boot-starter-parent
                2.7.10
                
            
              
                8
                8
                3.1.6
                2021.0.4.0
                UTF-8
            
             
               
                    org.springframework.boot
                    spring-boot-starter
                
                
                    org.springframework.boot
                    spring-boot-starter-web
                
                
                    com.alibaba.cloud
                    spring-cloud-starter-alibaba-nacos-discovery
                    ${springcloudalibaba.version}
                    
                        
                            
                            com.alibaba.nacos
                            nacos-client
                        
                    
                
                
                    com.alibaba.cloud
                    spring-cloud-starter-alibaba-nacos-config
                    ${springcloudalibaba.version}
                    
                        
                            
                            com.alibaba.nacos
                            nacos-client
                        
                    
                
                
                    org.springframework.cloud
                    spring-cloud-starter-bootstrap
                    ${springcloud.version}
                
                
                    com.alibaba.nacos
                    nacos-client
                    2.1.1
                
                
                    org.springframework.cloud
                    spring-cloud-starter-openfeign
                    ${springcloud.version}
                
                
                    org.springframework.cloud
                    spring-cloud-starter-loadbalancer
                    ${springcloud.version}
                
                 
                        org.springframework.cloud
                        spring-cloud-starter-netflix-hystrix
                        2.2.9.RELEASE
                    
                 
                    org.springframework.cloud
                    spring-cloud-starter-gateway
                    3.1.5
                
                 
                    org.springframework.boot
                    spring-boot-starter-web
                    
                        
                            org.springframework
                            spring-webmvc
                        
                        
                            spring-boot-starter-tomcat
                            org.springframework.boot
                        
                    
                
             
        

        环境准备

        未安装Nacos可先进行简单的单机部署

        nacos搭建Nacos standalone单机搭建部署

        配置

        配置文件

        application.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/**
        

        跨域配置

        对于所有GET请求的路径,来自docs.spring.io的请求都将允许CORS请求。

        要为未被某些网关路由谓词处理的请求提供相同的CORS配置,请将属性spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping设置为true。当尝试支持CORS预检请求并且您的路由谓词未评估为true时,这很有用,因为http方法为options。

        spring:
          cloud:
            gateway:
              globalcors:
                corsConfigurations:
                  '[/**]':
                    allowedOrigins: "https://docs.spring.io"
                    allowedMethods:
                    - GET
        

        bootstrap.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所配置的权重进行负载均衡调用,随机权重算法

        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 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 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 serviceInstanceListSupplierProvider, String serviceId, int seedPosition) {
                this.serviceId = serviceId;
                this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
                this.position = new AtomicInteger(seedPosition);
            }
            @SuppressWarnings("rawtypes")
            @Override
            public Mono choose(Request request) {
                ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
                return supplier.get(request).next().map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
            }
            private Response processInstanceResponse(ServiceInstanceListSupplier supplier, List serviceInstances) {
                Response serviceInstanceResponse = getInstanceResponse(serviceInstances);
                if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
                    ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
                }
                return serviceInstanceResponse;
            }
            /**
             * 按nacos权重
             *
             * @return
             */
            private Response getInstanceResponse(List serviceInstances) {
                Map 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 instances = serviceInstances.stream().map(i -> {
                    Instance instance = new Instance();
                    instance.setInstanceId(i.getInstanceId());
                    Map 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());
            }
        }
        

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

        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 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

        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();
            }
        }
        

        全局过滤器

        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 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一定要正确

        spring.cloud.nacos.config.prefix=gateway
        spring.cloud.nacos.config.file-extension=properties
        spring.profiles.active=dev
        
微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon