拓展Ribbon,集成nacos实现权重负载均衡

一、权重负载均衡的意义

实现的意义就是就集群的服务器的性能、配置可能不一样,一般都默认轮询的负载均衡的模式,但是这样会造成性能好的服务器工作的少,而性能差的服务器负荷过重,造成一种极为不公平的现象,所以我们就采用权重实现负载均衡,能者多劳。

二、 集成nacos 实现权重负载均衡

如下,我们发现在nacos的集群中,有权重的配置,但是配置了,并没有生效(自己到网关中测试)。
拓展Ribbon,集成nacos实现权重负载均衡_第1张图片

2.1、自己实现

直接上实现的代码

/**
 * @author ly
 */
@Configuration
public class NacosWeightRule extends AbstractLoadBalancerRule {

    private static final Log log = LogFactory.getLog(NacosWeightRule.class);

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }

    @Override
    public Server choose(Object key) {
        DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
        //服务名称
        String name = loadBalancer.getName();
        try {
            //组名称
            String groupName = discoveryProperties.getGroup();
            Instance instance = discoveryProperties.namingServiceInstance()
                    .selectOneHealthyInstance(name, groupName);

            log.info("选中的instance = {}" + instance);
            /*
             * instance转server的逻辑参考自:
             * org.springframework.cloud.alibaba.nacos.ribbon.NacosServerList.instancesToServerList
             */
            return new NacosServer(instance);

        } catch (NacosException e) {
            e.printStackTrace();
            return null;
        }
    }

2.2、NacosRule

这个类是是nacos实现的,通过进入源码会发现,其实和上边最后走的一段权重代码。

2.3、 权重源码分析

进入 selectOneHealthyInstance >Balancer.RandomByWeight.selectHost 代码中,会看到getHostByRandomWeight方法

refresh

 double[] exactWeights = new double[items.size()];
            int index = 0;
            for (Pair<T> item : itemsWithWeight) {
                double singleWeight = item.weight();
                //ignore item which weight is zero.see test_randomWithWeight_weight0 in ChooserTest
                if (singleWeight <= 0) {
                    continue;
                }
                exactWeights[index++] = singleWeight / originWeightSum;
            }

            weights = new double[items.size()];
            double randomRange = 0D;
            for (int i = 0; i < index; i++) {
                weights[i] = randomRange + exactWeights[i];
                randomRange += exactWeights[i];
            }

这个方法会将集群的节点,按照权重的比例存到Chooser的weights中,0的位置第一个节点的权重比例,第二个为node1+node2的权重比例,最后一个位置是1

randomWithWeight

 public T randomWithWeight() {
        Ref<T> ref = this.ref;
        double random = ThreadLocalRandom.current().nextDouble(0, 1);
        int index = Arrays.binarySearch(ref.weights, random);
        if (index < 0) {
            index = -index - 1;
        } else {
            return ref.items.get(index);
        }

        if (index >= 0 && index < ref.weights.length) {
            if (random < ref.weights[index]) {
                return ref.items.get(index);
            }
        }

        /* This should never happen, but it ensures we will return a correct
         * object in case there is some floating point inequality problem
         * wrt the cumulative probabilities. */
        return ref.items.get(ref.items.size() - 1);
    }

重要的方法

  1. ThreadLocalRandom.current().nextDouble(0, 1) 生成 一个随机数,范围是0-1(不包括1)
  2. binarySearch() 二分查找,根据随机数在数组中查找,

2.4 知识积累

2.4.1 binarySearch()

https://blog.csdn.net/qpzkobe/article/details/78897762

2.4.2、 intern()

 https://blog.csdn.net/qq_39662660/article/details/88396259 

NamingUtils 类中 getGroupedName 用到了这个方法,记录下来

   @Override
    public Instance selectOneHealthyInstance(String serviceName, String groupName, List<String> clusters, boolean subscribe) throws NacosException {

        if (subscribe) {
            return Balancer.RandomByWeight.selectHost(
                hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")));
        } else {
            return Balancer.RandomByWeight.selectHost(
                hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")));
        }
    }
 public static String getGroupedName(String serviceName, String groupName) {
        StringBuilder resultGroupedName = new StringBuilder()
            .append(groupName)
            .append(Constants.SERVICE_INFO_SPLITER)
            .append(serviceName);
        return resultGroupedName.toString().intern();
    }

你可能感兴趣的:(springCloud,#,nacos)