1.前言

本文基于上一节内容继续讲解,上一节内容传送门:Ribbon的介绍和使用(一)

2.Ribbon 支持Nacos权重

Ribbon内置的负载均衡规则是不支持Nacos权重的,这里我们编码写个扩展规则实现权重。

2.1 编写负载均衡规则扩展Ribbon
package com.ding.contentcenter.configuration;

import com.alibaba.nacos.api.exception.NacosException;
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.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;

@Slf4j
public class NacosWeightedRule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        // 读取配置文件,并初始化NacosWeightedRule
    }

    @Override
    public Server choose(Object o) {
        try {
            // ribbon入口,BaseLoadBalancer获取get方法
            BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();

            //想要请求的微服务的名称
            String name = loadBalancer.getName();

            // 拿到服务发现的相关api
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();

            // nacos client 自动通过基于权重的负载均衡算法选择一个实例
            Instance instance = namingService.selectOneHealthyInstance(name);
            log.info("port = {},instance={}",instance.getPort(),instance);
            return new NacosServer(instance);
        } catch (NacosException e) {
            return null;
        }
    }
}
2.2 编写配置
package ribbonconfiguration;

import com.ding.contentcenter.configuration.NacosWeightedRule;
import com.netflix.loadbalancer.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RibbonConfiguration {
    @Bean
    public IRule ribbonRule(){
        return new NacosWeightedRule();
    }
}
2.3 设置端口实例权重

ribbon权重调用_java

将端口为8888的实例权重设置为0

2.4 请求用户中心
curl --location --request GET 'http://localhost:8082/shares/1'

查看用户中心2个端口实例的响应情况:

ribbon权重调用_ribbon权重调用_02

ribbon权重调用_spring cloud_03

结论:因为端口为8888的实例权重为0,而端口为8080的权重为1,因此所有的请求都分配到了端口8080的实例上了,从而实现了基于权重的负载均衡算法。

注意:Nacos client内置了基于权重的负载均衡算法,而Spring Cloud Alibaba还要去整合Ribbon,这里是为了符合Spring Cloud的标准,其子项目Spring Cloud Commons定义了一些标准,而其子项目Spring Cloud Loadbalancer没有权重,因此Spring Cloud Alibaba为了遵循标准整合了Ribbon。

3.扩张Ribbon同一集群优先调用

为了容灾我们往往会将微服务的集群部署在不同的地方。为了提高性能,相同集群下的服务就会优先相互调用,如果当前集群下的服务无法响应时将会自动调用其他地区的服务。

3.1 编写同一集群优先调用规则
package com.ding.contentcenter.configuration;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.client.naming.core.Balancer;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object o) {
        try {
            // 拿到配置中的集群名称 SuZhou
            String clusterName = nacosDiscoveryProperties.getClusterName();

            // ribbon入口,BaseLoadBalancer获取get方法
            BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();

            //想要请求的微服务的名称
            String name = loadBalancer.getName();

            // 拿到服务发现的相关api,如:com.alibaba.nacos.client.naming.NacosNamingService@3194e0d0
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();

            // 1.找到指定服务的所有实例A
            List<Instance> instances = namingService.selectInstances(name,true);

            // 2.筛选出实例A与当前配置集群名称SuZhou相同的实例 B
            List<Instance> sameClustInstances = instances.stream()
                    .filter(instance -> Objects.equals(instance.getClusterName(),clusterName))
                    .collect(Collectors.toList());
            // 3.如果B是是空,就用A
            List<Instance> instancesToBeChosen = new ArrayList<>();
            if(CollectionUtils.isEmpty(sameClustInstances)){
                instancesToBeChosen = instances;
                log.warn("跨集群的调用,name={},clusterName={},instances={}",
                        name,
                        clusterName,
                        instances);
            }else{
                instancesToBeChosen = sameClustInstances;
            }
            // 4.基于权重的负载均衡算法,返回1个实例
            Instance instance = ExtendBalancer.getHostByRandomWeightw(instancesToBeChosen);
            log.info("选择的实例是: port{},instance{}",instance.getPort(),instance);

            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("error",e);
            return null;
        }
    }
}

class ExtendBalancer extends Balancer{
    public static Instance getHostByRandomWeightw(List<Instance> hosts) {
        return getHostByRandomWeight(hosts);
    }
}
3.2 编写配置
package ribbonconfiguration;

import com.ding.contentcenter.configuration.NacosSameClusterWeightedRule;
import com.netflix.loadbalancer.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RibbonConfiguration {
    @Bean
    public IRule ribbonRule(){
        return new NacosSameClusterWeightedRule();
    }
}
3.3 编写内容中心的配置
cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        # 当前集群:Suzhou
        cluster-name: Suzhou

内容中心的当前集群是:Suzhou

3.4 编写用户中心的配置
cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        cluster-name: SuZhou
  application:
    # 服务名称尽量用-,不用_和特殊字符
   name: user-center
server:
  port: 8080

用户中心端口为8080的实例所在集群为Suzhou,端口为8888的实例未设置所属集群。

3.5 内容中心多次请求用户中心
curl --location --request GET 'http://localhost:8082/shares/1'
3.6 响应结果

ribbon权重调用_权重_04

结论:通过以上编码编码实现了:同一集群的服务优先调用,当前集群的服务按照权重优先调用,当当前集群的服务失去响应会按照权重规则调用其他集群的服务。

4.Nacos的Namespace
4.1 新建命名空间

ribbon权重调用_权重_05

4.2 用户中心配置
cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        cluster-name: SZ
        namespace: 831b9597-fb98-4742-aafb-27986c142d2d
  application:
    # 服务名称尽量用-,不用_和特殊字符
   name: user-center
4.3 内容中心配置
cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        # 当前集群:SZ
        cluster-name: SZ
  application:
    # 服务名称尽量用-,不用_和特殊字符
    name: content-center

说明:通过namespace可以实现服务之前的隔离,不同命名空间下的服务不可以相互调用。