一,实现原理
利用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类 ,更复杂的情况可以依例扩展
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
?调用者和被调用者添加元数据
{
????"preserved.register.source": "SPRING_CLOUD",
????"version":"v2"
}
|