spring cloud gateway + nacos 灰度发布

原理

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

应用列表

nacos-user : 普通应用nacos-gateway

nacos-gateway

引入依赖

org.springframework.boot

spring-boot-starter-actuator

org.springframework.cloud

spring-cloud-starter-bootstrap

com.alibaba.cloud

spring-cloud-starter-alibaba-nacos-config

com.alibaba.cloud

spring-cloud-starter-alibaba-nacos-discovery

org.springframework.cloud

spring-cloud-starter-netflix-ribbon

org.springframework.cloud

spring-cloud-starter-gateway

org.springframework.boot

spring-boot-starter-test

test

org.springframework.cloud

spring-cloud-loadbalancer

注意一定要引入 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 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 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 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)

.flatMap(list -> {

RequestDataContext context = (RequestDataContext) request.getContext();

String version = context.getClientRequest().getHeaders().getFirst(GREP_HEADER);

boolean b = StringUtils.hasText(version) ? true : false;

List 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 processInstanceResponse(ServiceInstanceListSupplier supplier,

List serviceInstances) {

Response serviceInstanceResponse = getInstanceResponse(serviceInstances);

if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {

((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());

}

return serviceInstanceResponse;

}

private Response getInstanceResponse(List 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);

}

}

配置

@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 greyLoadBalancer(Environment environment,

LoadBalancerClientFactory loadBalancerClientFactory) {

String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);

return new GreyRoundRobinLoadBalancer(

loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);

}

}

向容器注入自定义 LoadBalancer:

@Configuration

@LoadBalancerClients(defaultConfiguration = GreyLoadBalancerClientConfiguration.class)

public class CusLoadBalancerClientConfiguration {

}

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

使用

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

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

参考 nacos loadbalancer:

@Configuration(proxyBeanMethods = false)

@EnableConfigurationProperties

@ConditionalOnLoadBalancerNacos

@ConditionalOnNacosDiscoveryEnabled

@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerClientConfiguration.class)

public class LoadBalancerNacosAutoConfiguration {

}

good luck!

好文链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: