一個微服務在線上可能多版本共存,例如:
- 服務提供者有兩個版本:v1、v2
- 服務消費者也有兩個版本:v1、v2
v1/v2是不兼容的。
1、服務消費者v1只能調用服務提供者v1;消費者v2只能調用提供者v2。
2、優先調用同集羣下的實例。
3、實現基於權重配置的負載均衡。
如何實現呢?
下面圍繞該場景,實現微服務之間的版本控制。
元數據
元數據就是一堆的描述信息,以map存儲。舉個例子:
spring:
cloud:
nacos:
metadata:
# 自己這個實例的版本
version: v1
# 允許調用的提供者版本
target-version: v1
需求分析
我們需要實現的有兩點:
- 優先選擇同集羣下,符合metadata的實例
- 如果同集羣加沒有符合metadata的實例,就選擇所有集羣下,符合metadata的實例
寫代碼
@Slf4j
public class NacosFinalRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public Server choose(Object key) {
// 負載均衡規則:優先選擇同集羣下,符合metadata的實例
// 如果沒有,就選擇所有集羣下,符合metadata的實例
// 1. 查詢所有實例 A
// 2. 篩選元數據匹配的實例 B
// 3. 篩選出同cluster下元數據匹配的實例 C
// 4. 如果C爲空,就用B
// 5. 隨機選擇實例
try {
String clusterName = this.nacosDiscoveryProperties.getClusterName();
String targetVersion = this.nacosDiscoveryProperties.getMetadata().get("target-version");
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
String name = loadBalancer.getName();
NamingService namingService = this.nacosDiscoveryProperties.namingServiceInstance();
// 所有實例
List<Instance> instances = namingService.selectInstances(name, true);
List<Instance> metadataMatchInstances = instances;
// 如果配置了版本映射,那麼只調用元數據匹配的實例
if (StringUtils.isNotBlank(targetVersion)) {
metadataMatchInstances = instances.stream()
.filter(instance -> Objects.equals(targetVersion, instance.getMetadata().get("version")))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(metadataMatchInstances)) {
log.warn("未找到元數據匹配的目標實例!請檢查配置。targetVersion = {}, instance = {}", targetVersion, instances);
return null;
}
}
List<Instance> clusterMetadataMatchInstances = metadataMatchInstances;
// 如果配置了集羣名稱,需篩選同集羣下元數據匹配的實例
if (StringUtils.isNotBlank(clusterName)) {
clusterMetadataMatchInstances = metadataMatchInstances.stream()
.filter(instance -> Objects.equals(clusterName, instance.getClusterName()))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(clusterMetadataMatchInstances)) {
clusterMetadataMatchInstances = metadataMatchInstances;
log.warn("發生跨集羣調用。clusterName = {}, targetVersion = {}, clusterMetadataMatchInstances = {}", clusterName, targetVersion, clusterMetadataMatchInstances);
}
}
Instance instance = ExtendBalancer.getHostByRandomWeight2(clusterMetadataMatchInstances);
return new NacosServer(instance);
} catch (Exception e) {
log.warn("發生異常", e);
return null;
}
}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
}
負載均衡算法:
public class ExtendBalancer extends Balancer {
/**
* 根據權重,隨機選擇實例
*
* @param instances 實例列表
* @return 選擇的實例
*/
public static Instance getHostByRandomWeight2(List<Instance> instances) {
return getHostByRandomWeight(instances);
}
}