文章目录
- Nacos领域模型及负载均衡调用策略
- 1.前言
- 2.Nacos领域模型划分
- 3.同集群优先调用策略
- 4.金丝雀版本权重负载均衡策略
- 5.结语
1.前言
阿里,阿里爸爸,阿里爸爸开源的框架真的很精髓~
2.Nacos领域模型划分
- NameSpace:命名空间,默认的NameSpace是public。比如,我们开发,测试环境共用一个nacos,必定我们的接口地址也是不同,而且你在开发过程中,也是不建议随意配置测试环境的,这时我们就应该用namespace来隔离我们的空间。
- group:分组。也是用来隔离的,打个比方啊,加入我们的用户服务,订单服务,仓储服务和物流服务四个服务,订单服务中有一个接口叫getData,仓储服务中也有一个接口叫getData,我们的用户服务只想调用到我们的订单服务的getData,不想调用到仓储服务的getData,这时我们可以用group分组来隔离。
- cluster:集群。打个比方,我们现在有两组集群,一组是北京的订单服务集群,北京的商品服务集群,还有一组是南京的订单服务集群,南京的商品服务集群。 我们希望北京的订单集群,优先去调用北京的商品系统,南京的优先调用南京的集群服务。并不希望我们跨地区远程调用(如果组内实在没有服务了,也可以调用,但是优先考虑同一集群的)
- metadata: 只要用于版本控制。比如,我们在开发中可能是多个版本共存的,订单的v1版本只能调商品的v1版本,订单的v2版本只能调商品的v2版本。
yml配置
spring:
application:
name: test-demo
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 0610f97e-c25d-4f49-bfb8-b340e3584b82
group: test
cluster-name: BJ-cluster
metadata:
current-version: V1
server:
port: 8888
3.同集群优先调用策略
@Slf4j
public class TheSameClusterPriorityRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties discoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
@Override
public Server choose(Object key) {
try {
//第一步:获取当前服务所在的集群
String currentClusterName = discoveryProperties.getClusterName();
//第二步:获取一个负载均衡对象
BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) getLoadBalancer();
//第三步:获取当前调用的微服务的名称
String invokedSerivceName = baseLoadBalancer.getName();
//第四步:获取nacos clinet的服务注册发现组件的api
NamingService namingService = discoveryProperties.namingServiceInstance();
//第五步:获取所有的服务实例 包括不同集群的
List<Instance> allInstance = namingService.getAllInstances(invokedSerivceName);
List<Instance> theSameClusterNameInstList = new ArrayList<>();
//第六步:过滤筛选同集群下的所有实例
for(Instance instance : allInstance) {
if(StringUtils.endsWithIgnoreCase(instance.getClusterName(),currentClusterName)) {
theSameClusterNameInstList.add(instance);
}
}
Instance toBeChooseInstance ;
//第七步:选择合适的一个实例调用
if(theSameClusterNameInstList.isEmpty()) {
toBeChooseInstance = TulingWeightedBalancer.chooseInstanceByRandomWeight(allInstance); //allInstance 所有实例(包括不同集群的)
log.info("发生跨集群调用--->当前微服务所在集群:{},被调用微服务所在集群:{},Host:{},Port:{}",
currentClusterName,toBeChooseInstance.getClusterName(),toBeChooseInstance.getIp(),toBeChooseInstance.getPort());
}else {
toBeChooseInstance = TulingWeightedBalancer.chooseInstanceByRandomWeight(theSameClusterNameInstList);// theSameClusterNameInstList 通过集群实例
log.info("同集群调用--->当前微服务所在集群:{},被调用微服务所在集群:{},Host:{},Port:{}",
currentClusterName,toBeChooseInstance.getClusterName(),toBeChooseInstance.getIp(),toBeChooseInstance.getPort());
}
return new NacosServer(toBeChooseInstance);
} catch (NacosException e) {
log.error("同集群优先权重负载均衡算法选择异常:{}",e);
}
return null;
}
}
yml ribbon的 细粒度配置
product-center: # 需要配置的微服务名称
ribbon:
NFLoadBalancerRuleClassName: com.demo.myrule.TheSameClusterPriorityWithVersionRule
4.金丝雀版本权重负载均衡策略
金丝雀版本发布,即灰度发布,同版本同集群优先调用
@Slf4j
public class TheSameClusterPriorityWithVersionRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties discoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
@Override
public Server choose(Object key) {
try {
String currentClusterName = discoveryProperties.getClusterName();
List<Instance> theSameClusterNameAndTheSameVersionInstList = getTheSameClusterAndTheSameVersionInstances(discoveryProperties);
//声明被调用的实例
Instance toBeChooseInstance;
//判断同集群同版本号的微服务实例是否为空
if(theSameClusterNameAndTheSameVersionInstList.isEmpty()) {
//跨集群调用相同的版本
toBeChooseInstance = crossClusterAndTheSameVersionInovke(discoveryProperties);
}else {
// theSameClusterNameAndTheSameVersionInstList 同集群同版本的实例
toBeChooseInstance = TulingWeightedBalancer.chooseInstanceByRandomWeight(theSameClusterNameAndTheSameVersionInstList);
log.info("同集群同版本调用--->当前微服务所在集群:{},被调用微服务所在集群:{},当前微服务的版本:{},被调用微服务版本:{},Host:{},Port:{}",
currentClusterName,toBeChooseInstance.getClusterName(),discoveryProperties.getMetadata().get("current-version"),
toBeChooseInstance.getMetadata().get("current-version"),toBeChooseInstance.getIp(),toBeChooseInstance.getPort());
}
return new NacosServer(toBeChooseInstance);
} catch (NacosException e) {
log.error("同集群优先权重负载均衡算法选择异常:{}",e);
return null;
}
}
/**
* 方法实现说明:获取相同集群下,相同版本的 所有实例
* @author:smlz
* @param discoveryProperties nacos的配置
* @return: List<Instance>
*/
private List<Instance> getTheSameClusterAndTheSameVersionInstances(NacosDiscoveryProperties discoveryProperties) throws NacosException {
//当前的集群的名称
String currentClusterName = discoveryProperties.getClusterName();
String currentVersion = discoveryProperties.getMetadata().get("current-version");// current-version 是配置在yml中的metadata下的
//获取所有实例的信息(包括不同集群的)
List<Instance> allInstance = getAllInstances(discoveryProperties);
List<Instance> theSameClusterNameAndTheSameVersionInstList = new ArrayList<>();
//过滤相同集群的
for(Instance instance : allInstance) {
if(StringUtils.endsWithIgnoreCase(instance.getClusterName(),currentClusterName)&&
StringUtils.endsWithIgnoreCase(instance.getMetadata().get("current-version"),currentVersion)) {
theSameClusterNameAndTheSameVersionInstList.add(instance);
}
}
return theSameClusterNameAndTheSameVersionInstList;
}
/**
* 方法实现说明:获取被调用服务的所有实例
* @author:smlz
* @param discoveryProperties nacos的配置
* @return: List<Instance>
*/
private List<Instance> getAllInstances(NacosDiscoveryProperties discoveryProperties) throws NacosException {
//第1步:获取一个负载均衡对象
BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) getLoadBalancer();
//第2步:获取当前调用的微服务的名称
String invokedSerivceName = baseLoadBalancer.getName();
//第3步:获取nacos clinet的服务注册发现组件的api
NamingService namingService = discoveryProperties.namingServiceInstance();
//第4步:获取所有的服务实例
List<Instance> allInstance = namingService.getAllInstances(invokedSerivceName);
return allInstance;
}
private Instance crossClusterAndTheSameVersionInovke(NacosDiscoveryProperties discoveryProperties) throws NacosException {
//获取所有集群下相同版本的实例信息
List<Instance> crossClusterAndTheSameVersionInstList = getCrossClusterAndTheSameVersionInstList(discoveryProperties);
//当前微服务的版本号
String currentVersion = discoveryProperties.getMetadata().get("current-version");
//当前微服务的集群名称
String currentClusterName = discoveryProperties.getClusterName();
//声明被调用的实例
Instance toBeChooseInstance = null ;
//没有对应相同版本的实例
if(crossClusterAndTheSameVersionInstList.isEmpty()) {
log.info("跨集群调用找不到对应合适的版本当前版本为:currentVersion:{}",currentVersion);
throw new RuntimeException("找不到相同版本的微服务实例");
}else {
//crossClusterAndTheSameVersionInstList 所有的,同版本不同集群
toBeChooseInstance = TulingWeightedBalancer.chooseInstanceByRandomWeight(crossClusterAndTheSameVersionInstList);
log.info("跨集群同版本调用--->当前微服务所在集群:{},被调用微服务所在集群:{},当前微服务的版本:{},被调用微服务版本:{},Host:{},Port:{}",
currentClusterName,toBeChooseInstance.getClusterName(),discoveryProperties.getMetadata().get("current-version"),
toBeChooseInstance.getMetadata().get("current-version"),toBeChooseInstance.getIp(),toBeChooseInstance.getPort());
}
return toBeChooseInstance;
}
/**
* 方法实现说明:跨集群环境下 相同版本的
* @author:smlz
* @param discoveryProperties
* @return: List<Instance>
*/
private List<Instance> getCrossClusterAndTheSameVersionInstList(NacosDiscoveryProperties discoveryProperties) throws NacosException {
//版本号
String currentVersion = discoveryProperties.getMetadata().get("current-version");
//被调用的所有实例
List<Instance> allInstance = getAllInstances(discoveryProperties);
List<Instance> crossClusterAndTheSameVersionInstList = new ArrayList<>();
//过滤相同版本
for(Instance instance : allInstance) {
if(StringUtils.endsWithIgnoreCase(instance.getMetadata().get("current-version"),currentVersion)) {
crossClusterAndTheSameVersionInstList.add(instance);
}
}
return crossClusterAndTheSameVersionInstList;
}
}
yml ribbon的 细粒度配置
order-center: # 需要配置的微服务名称
ribbon:
NFLoadBalancerRuleClassName: com.demo.myrule.TheSameClusterPriorityWithVersionRule