• spring cloud gateway + nacos 灰度发布


    spring cloud gateway + nacos 灰度发布

    原理

    • 在客户端请求时的 header 带入 一个标签,如: svc_version
    • 服务器应用 启动的时候 打上标签,可以和客户端的属性一致,如 svc_version
    • 请求到网关的时候,网关负载过滤带 svc_version 属性的服务器应用列表
    • 返回已经匹配客户端 svc_version 的服务器应用 列表
    • 在这些列表中请求转发客户端请求

    应用列表

    • nacos-user : 普通应用
    • nacos-gateway

    nacos-gateway

    引入依赖

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-actuatorartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-bootstrapartifactId>
    dependency>
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
    dependency>
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-netflix-ribbonartifactId>
            exclusion>
        exclusions>
    dependency>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-gatewayartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    dependency>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-loadbalancerartifactId>
    dependency>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    注意一定要引入 spring-cloud-loadbalancer!!

    实现 灰度负载LoadBalancer

    public class GreyRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    
    	private static final String GREP_HEADER = "svc_version";
    
    	private static final Log log = LogFactory.getLog(GreyRoundRobinLoadBalancer.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 GreyRoundRobinLoadBalancer(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 GreyRoundRobinLoadBalancer(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)
    				.flatMap(list -> {
    					RequestDataContext context = (RequestDataContext) request.getContext();
    					String version  = context.getClientRequest().getHeaders().getFirst(GREP_HEADER);
    					boolean b = StringUtils.hasText(version) ? true : false;
    					List<ServiceInstance> collect = list.stream().filter(instance -> {
    						if (b) {
    							return instance.getMetadata().containsKey(GREP_HEADER) && instance.getMetadata().get(GREP_HEADER).equals(version);
    						}
    						return !instance.getMetadata().containsKey(GREP_HEADER);
    					}).collect(Collectors.toList());
    					return Mono.justOrEmpty(collect);
    				})
    				.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();
    		}
    
    		// Ignore the sign bit, this allows pos to loop sequentially from 0 to
    		// Integer.MAX_VALUE
    		int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
    
    		ServiceInstance instance = instances.get(pos % instances.size());
    
    		return new DefaultResponse(instance);
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    配置

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnDiscoveryEnabled
    public class GreyLoadBalancerClientConfiguration {
    
        private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 173827465;
    
        @Bean
    //    @Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER)
        public ReactorLoadBalancer<ServiceInstance> greyLoadBalancer(Environment environment,
                                                                     LoadBalancerClientFactory loadBalancerClientFactory) {
            String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
            return new GreyRoundRobinLoadBalancer(
                    loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    向容器注入自定义 LoadBalancer:

    @Configuration
    @LoadBalancerClients(defaultConfiguration = GreyLoadBalancerClientConfiguration.class)
    public class CusLoadBalancerClientConfiguration {
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    @LoadBalancerClients, 重要!!,给所有的负载都加上自定义的灰度负载器

    使用

    启用应用nacos-user, 一个打了标签,一个没有打标签:

    在这里插入图片描述

    在客户端请求网关时,headers带上 svc_version=1.0, 那么请求就会打到 5558这个应用

    参考 nacos loadbalancer:

    @Configuration(proxyBeanMethods = false)
    @EnableConfigurationProperties
    @ConditionalOnLoadBalancerNacos
    @ConditionalOnNacosDiscoveryEnabled
    @LoadBalancerClients(defaultConfiguration = NacosLoadBalancerClientConfiguration.class)
    public class LoadBalancerNacosAutoConfiguration {
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    good luck!

  • 相关阅读:
    【2023秋招面经】4399 前端 二面-hr面(20min)
    ​kali渗透测试环境搭建
    网络安全(黑客)自学
    7.20日 ksjsb上车说明及注意事项
    Biotin-NHS LC(72040-63-2)生物素接头|站点特定探针
    测试框架gtest以及内存泄漏检测
    最全指令系统详解
    SSIM公式:结构相似性计算原理,基于SSIM的图像质量评价
    如何创建Vue项目并与后端django联调
    别再当大冤种了,揭开3D建模报班6个常见套路
  • 原文地址:https://blog.csdn.net/u013887008/article/details/126133351