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

05-13 1111阅读

文章目录

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

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

      常见Gateway架构

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

      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可以对请求和响应进行处理。

        工作机制

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

        客户端向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服务列表

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

        服务权重配置

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

        服务测试代码

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

        负载效果

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

        配置中心测试代码

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

        本地配置项

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

        配置中心配置

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

        效果

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

        配置中心注意:

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

        spring.cloud.nacos.config.prefix=gateway
        spring.cloud.nacos.config.file-extension=properties
        spring.profiles.active=dev
        
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]