table of Contents
Implemented in Spring Cloud Gateway
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<>();
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
&& input.getServer() instanceof NacosServer
&& 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
●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