一,实现原理

利用nacos元数据,给服务发起方和被调用方定义标签。相同标签的优先调用。

二,实现场景

假设 A,B两个服务,各存在4个节点,且版本都是V1

A1(V1)  B1(V1)

A2(V1)  B2(V1)

A3(V1)  B3(V1)

A4(V1)  B4(V1)

更新版本后

A1(V2)  B1(V2)

A2(V1)  B2(V1)

A3(V1)  B3(V1)

A4(V1)  B4(V1)

更新后V2的相互调用,V1的相互调用。

三,关键代码

参考NacosRule类 ,更复杂的情况可以依例扩展

基于nacos netflix loadbalancer灰度发布策略_spring

pom.xml

<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>


GrayConstant.java


package com.rutron.canary;

/**
* @author liwenchao
*/
public class GrayConstant {

public static final String GRAY_TAG = "version";

}


CanaryBalancerRule.java


package com.rutron.canary;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.ribbon.ExtendBalancer;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* @author liwenchao
*/
@Slf4j
public class CanaryBalancerRule extends AbstractLoadBalancerRule {

@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;

@Autowired
private NacosServiceManager nacosServiceManager;

public CanaryBalancerRule() {

}

@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {

}

@Override
public Server choose(Object key) {
try {
String clusterName = this.nacosDiscoveryProperties.getClusterName();
String group = this.nacosDiscoveryProperties.getGroup();
Map<String, String> ribbonAttributes = this.nacosDiscoveryProperties.getMetadata();
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
String serviceName = loadBalancer.getName();
NamingService namingService = this.nacosServiceManager.getNamingService(this.nacosDiscoveryProperties.getNacosProperties());
List<Instance> allInstances = namingService.selectInstances(serviceName, group, true);
List<Instance> grayInstances = new ArrayList<>();
List<Instance> noneGrayInstances = new ArrayList<>();
Instance toBeChooseInstance;
if (CollectionUtils.isEmpty(allInstances)) {
log.warn("no instance in service {}", serviceName);
return null;
} else {
if (StringUtils.isNotBlank(clusterName)) {
for (Instance instance : allInstances) {
Map<String, String> metadata = instance.getMetadata();
//当前服务的灰度标签和被调用的服务的灰度标签比,相同则灰色
if (!ribbonAttributes.containsKey(GrayConstant.GRAY_TAG) || !metadata.containsKey(GrayConstant.GRAY_TAG)) {
noneGrayInstances.add(instance);
} else if (ribbonAttributes.get(GrayConstant.GRAY_TAG).trim().equalsIgnoreCase(metadata.get(GrayConstant.GRAY_TAG).trim())) {
grayInstances.add(instance);
} else if (!StringUtils.isBlank(metadata.get(GrayConstant.GRAY_TAG))) {
noneGrayInstances.add(instance);
}
}
}
if (grayInstances.size() > 0) {
toBeChooseInstance = ExtendBalancer.getHostByRandomWeight2(grayInstances);
return new NacosServer(toBeChooseInstance);
}
if (noneGrayInstances.size() > 0) {
toBeChooseInstance = ExtendBalancer.getHostByRandomWeight2(noneGrayInstances);
} else {
toBeChooseInstance = ExtendBalancer.getHostByRandomWeight2(allInstances);
}
return new NacosServer(toBeChooseInstance);
}
} catch (Exception e) {
log.warn("NacosRule error", e);
return null;
}
}
}


RibbonAutoConfiguration.java


package com.rutron.canary;

import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;

/**
* @author liwenchao
*/
public class RibbonAutoConfiguration {

/**
* 全局配置指定负载均衡策略
*/
@Bean
public IRule canaryBalancerRule() {
return new CanaryBalancerRule();
}
}

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.rutron.canary.RibbonAutoConfiguration

基于nacos netflix loadbalancer灰度发布策略_java_02

 调用者和被调用者添加元数据

{
"preserved.register.source": "SPRING_CLOUD",
"version":"v2"
}