Spring Cloud Gateway extension supports multi-version control and grayscale release

table of Contents

Grayscale release

Implementation plan

nginx + lua (openresty)

Netflix Zuul

Implemented in Spring Cloud Gateway

Integrate nacos


Grayscale release

What is gray release, please refer to the concept . Let’s take a look at the following picture. In layman’s terms: In order to ensure a smooth transition of the service upgrade process and improve customer experience, some users and some users will be updated progressively. Multiple versions of the client appear at the same time, in order to ensure the availability of multiple versions of the client, multiple versions of the corresponding server version are required. Gray release is to ensure that multiple versions of clients and servers can correctly correspond to each other through certain strategies.

The so-called gray release, that is, when there are multiple instances of a service, and the versions of the instance versions are not consistent, pass

Implementation plan

nginx + lua (openresty)

 

Netflix Zuul

Only need to customize the ribbon assertion, the core is to get the version number in the upper and lower request header through TTL

@Slf4j
public class MetadataCanaryRuleHandler extends ZoneAvoidanceRule {

    @Override
    public AbstractServerPredicate getPredicate() {
        return new AbstractServerPredicate() {
            @Override
            public boolean apply(PredicateKey predicateKey) {
                String targetVersion = RibbonVersionHolder.getContext();
                RibbonVersionHolder.clearContext();
                if (StrUtil.isBlank(targetVersion)) {
                    log.debug("客户端未配置目标版本直接路由");
                    return true;
                }

                DiscoveryEnabledServer server = (DiscoveryEnabledServer) predicateKey.getServer();
                final Map<string, string> metadata = server.getInstanceInfo().getMetadata();
                if (StrUtil.isBlank(metadata.get(SecurityConstants.VERSION))) {
                    log.debug("当前微服务{} 未配置版本直接路由");
                    return true;
                }

                if (metadata.get(SecurityConstants.VERSION).equals(targetVersion)) {
                    return true;
                } else {
                    log.debug("当前微服务{} 版本为{},目标版本{} 匹配失败", server.getInstanceInfo().getAppName()
                            , metadata.get(SecurityConstants.VERSION), targetVersion);
                    return false;
                }
            }
        };
    }
}

Version number in maintenance request

public class RibbonVersionHolder {
    private static final ThreadLocal<string> context = new TransmittableThreadLocal&lt;&gt;();

    public static String getContext() {
        return context.get();
    }

    public static void setContext(String value) {
        context.set(value);
    }

    public static void clearContext() {
        context.remove();
    }
}

Implemented in Spring Cloud Gateway

The first response, refer to the implementation of zuul, customize the assertion, and then get the version information from the top and bottom. However, because spring cloud gateway is based on webflux reactive programming, traditional TTL or RequestContextHolder cannot maintain context requests correctly.

First, let's understand the process of load balancing . After a request reaches the gateway, the corresponding service name will be parsed, and then all available instances of the service will be obtained, and then our filtering method will be called to filter out all service instances available for the request, and finally the polling load balancing will be performed.

Let's first look at spring clou's gateway's default lb strategy to implement LoadBalancerClientFilter

public class LoadBalancerClientFilter implements GlobalFilter, Ordered {
	@Override
	public int getOrder() {
		return LOAD_BALANCER_CLIENT_FILTER_ORDER;
	}

	@Override
	@SuppressWarnings("Duplicates")
	public Mono<void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		return chain.filter(exchange);
	}

	protected ServiceInstance choose(ServerWebExchange exchange) {
		return loadBalancer.choose(
				((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost());
	}
}

We only need to rewrite the choose method to pass the context request to the routing assertion, as follows

@Override
protected ServiceInstance choose(ServerWebExchange exchange) {
	HttpHeaders headers = exchange.getRequest().getHeaders();
	return loadBalancer.choose(((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost(), headers);
}

Then you can get it through PredicateKey in the routing assertion

public abstract class AbstractDiscoveryEnabledPredicate extends AbstractServerPredicate {

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean apply(@Nullable PredicateKey input) {
		return input != null
				&amp;&amp; input.getServer() instanceof NacosServer
				&amp;&amp; apply((NacosServer) input.getServer(), (HttpHeaders) input.getLoadBalancerKey());
	}
}

Finally calculated based on the version

    public class GrayMetadataAwarePredicate extends AbstractDiscoveryEnabledPredicate {

	@Override
	protected boolean apply(NacosServer server, HttpHeaders headers) {
		PigxRibbonRuleProperties ribbonProperties = SpringContextHolder.getBean(PigxRibbonRuleProperties.class);

		if (!ribbonProperties.isGrayEnabled()) {
			log.debug("gray closed,GrayMetadataAwarePredicate return true");
			return true;
		}

		final Map<string, string> metadata = server.getMetadata();
		String version = metadata.get(CommonConstants.VERSION);
		// 判断Nacos服务是否有版本标签
		if (StrUtil.isBlank(version)) {
			log.debug("nacos server tag is blank ,GrayMetadataAwarePredicate return true");
			return true;
		}

		// 判断请求中是否有版本
		String target = headers.getFirst(CommonConstants.VERSION);
		if (StrUtil.isBlank(target)) {
			log.debug("request headers version is blank,GrayMetadataAwarePredicate return true");
			return true;
		}

		log.debug("请求版本:{} ,当前服务版本:{}", target, version);
		return target.equals(version);
	}

}

Integrate nacos

Combining the dynamic configuration of nacos can easily achieve grayscale

 

original

●The strongest Tomcat8 performance optimization in history

Why can Alibaba resist 10 billion in 90 seconds? --The evolution of server-side high-concurrency distributed architecture

B2B e-commerce platform--ChinaPay UnionPay electronic payment function

Learn Zookeeper distributed lock, let interviewers look at you with admiration

SpringCloud e-commerce spike microservice-Redisson distributed lock solution

Check out more good articles, enter the official account--please me--excellent in the past

A deep and soulful public account 0.0

Guess you like

Origin blog.csdn.net/a1036645146/article/details/109442277