• 【Dubbo】负载均衡


    一. 概述

    版本:2.7.8

    解决问题

    • 当有多个服务提供者时,避免请求集中到其中一个或多个,导致负载过高,服务不可用,需要做一定的负载均衡策略。
    • Dubbo提供了多种均衡策略,默认为random,也就是每次加权随机调用一台服务提供者的服务。

    二. Dubbo负载均衡模式

    Random LoadBalance:加权随机

    • 按照概率设置权重,比较均匀,并且可以动态调节提供者的权重。

    RoundRobin LoadBalance:轮询

    • 轮询,按公约后的权重设置轮询比率。会存在执行比较慢的服务提供者堆积请求的情况,比如一个机器执行得非常慢,但是机器没有宕机(如果宕机了,那么当前机器会从ZooKeeper 的服务列表中删除)。
    • 当很多新的请求到达该机器后,由于之前的请求还没处理完,会导致新的请求被堆积,久而久之,消费者调用这台机器上的所有请求都会被阻塞。

    LeastActive LoadBalance:最少活跃调用数

    • 如果每个提供者的活跃数相同,则随机选择一个。
    • 在每个服务提供者里维护着一个活跃数计数器,用来记录当前同时处理请求的个数,也就是并发处理任务的个数。这个值越小,说明当前服务提供者处理的速度越快或者当前机器的负载比较低,所以路由选择时就选择该活跃度最小的机器。
    • 如果一个服务提供者处理速度很慢,由于堆积,同时处理的请求就比较多,也就是说活跃调用数较大,处理速度慢。这时,处理速度慢的提供者将收到更少的请求。

    ConsistentHash LoadBalance一致性Hash策略

    • 一致性Hash,可以保证相同参数的请求总是发到同一提供者,当某一台提供者机器宕机时,原本发往该提供者的请求,将基于虚拟节点平摊给其他提供者,这样就不会引起剧烈变动。

    三. LoadBalance接口及实现类结构图

    在这里插入图片描述


    四. 源码解析

    1. 接口LoadBalance

    • 负载均衡模式默认为:random
    package org.apache.dubbo.rpc.cluster;
    
    @SPI(RandomLoadBalance.NAME)
    public interface LoadBalance {
    
        /**
         * select one invoker in list.
         */
        @Adaptive("loadbalance")
        <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
    
    }
    

    2. AbstractLoadBalance抽象类

    • 使用模板模式执行公共业务
    • 各个实现类实现抽象方法(doSelect)
    • getWeight:从URL中获取各个服务提供者的权重
    package org.apache.dubbo.rpc.cluster.loadbalance;
    
    public abstract class AbstractLoadBalance implements LoadBalance {
    
       @Override
        public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
            if (CollectionUtils.isEmpty(invokers)) {
                return null;
            }
            if (invokers.size() == 1) {
                return invokers.get(0);
            }
            return doSelect(invokers, url, invocation);
        }
    
        protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);
    
        /**
         * Get the weight of the invoker's invocation which takes warmup time into account
         * if the uptime is within the warmup time, the weight will be reduce proportionally
         *
         * @param invoker    the invoker
         * @param invocation the invocation of this invoker
         * @return weight
         */
        int getWeight(Invoker<?> invoker, Invocation invocation) {
    
            int weight;
            URL url = invoker.getUrl();
            // Multiple registry scenario, load balance among multiple registries.
            if (REGISTRY_SERVICE_REFERENCE_PATH.equals(url.getServiceInterface())) {
                weight = url.getParameter(REGISTRY_KEY + "." + WEIGHT_KEY, DEFAULT_WEIGHT);
            } else {
                weight = url.getMethodParameter(invocation.getMethodName(), WEIGHT_KEY, DEFAULT_WEIGHT);
                if (weight > 0) {
                    long timestamp = invoker.getUrl().getParameter(TIMESTAMP_KEY, 0L);
                    if (timestamp > 0L) {
                        long uptime = System.currentTimeMillis() - timestamp;
                        if (uptime < 0) {
                            return 1;
                        }
                        int warmup = invoker.getUrl().getParameter(WARMUP_KEY, DEFAULT_WARMUP);
                        if (uptime > 0 && uptime < warmup) {
                            weight = calculateWarmupWeight((int)uptime, warmup, weight);
                        }
                    }
                }
            }
            return Math.max(weight, 0);
        }
    }
    

    3. RandomLoadBalance加权随机算法

    RandomLoadBalance实现类

    
    package org.apache.dubbo.rpc.cluster.loadbalance;
    
    public class RandomLoadBalance extends AbstractLoadBalance {
    
        public static final String NAME = "random";
    
        /**
         * 加权随机从invokers中选择一个
         * @param invokers invokers集合
         * @param url URL
         * @param invocation Invocation
         * @param <T>
         * @return 选择的invoker
         */
        @Override
        protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    
            // Number of invokers
            int length = invokers.size();
            
            // Every invoker has the same weight?
            boolean sameWeight = true;
            
            // the weight of every invokers
            int[] weights = new int[length];
            
            // the first invoker's weight
            int firstWeight = getWeight(invokers.get(0), invocation);
            weights[0] = firstWeight;
    
            // The sum of weights
            int totalWeight = firstWeight;
            for (int i = 1; i < length; i++) {
                int weight = getWeight(invokers.get(i), invocation);
                // save for later use
                weights[i] = weight;
                // Sum
                totalWeight += weight;
                if (sameWeight && weight != firstWeight) {
                    sameWeight = false;
                }
            }
            if (totalWeight > 0 && !sameWeight) {
            
                // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
                // 使用 ThreadLocalRandom.current().nextInt(length)从所有服务提供者里随机选择一个服务提供者进行调用。需要注意的是,这里没有使用Random而是使用了ThreadLocalRandom,这是出于性能上的考虑,因 为Random在高并发下会导致大量线程竞争同一个原子变量,导致大量线程原地自旋,从而浪费CPU资源
                int offset = ThreadLocalRandom.current().nextInt(totalWeight);
                
                // Return a invoker based on the random value.
                for (int i = 0; i < length; i++) {
                    offset -= weights[i];
                    if (offset < 0) {
                        return invokers.get(i);
                    }
                }
            }
            // If all invokers have the same weight value or totalWeight=0, return evenly.
            return invokers.get(ThreadLocalRandom.current().nextInt(length));
        }
    }
    
    

    4. 其它模式(参考源码)


    五. 实现自定义LoadBalance

    1. 消费端使用

    2. 功能:自定义负载均衡实现

    3. 定义实现类

    MyLoadBalance实现类

    package org.apache.dubbo.rpc.cluster.loadbalance;
    
    public class MyLoadBalance extends AbstractLoadBalance {
    
        @Override
        protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    
            // 自定义复杂均衡算法,从invokers中选择一个返回
        }
    }
    

    引入SPI

    • resources新增文件夹META-INF.dubbo
    • 新建文件:org.apache.dubbo.rpc.cluster.LoadBalance
    • 文件内容:myLoadBalance=org.apache.dubbo.rpc.cluster.loadbalance.MyLoadBalance

    六. 使用自定义LoadBalance

    public static void main(String[] args) {
    
            // 1.创建服务引用对象实例
            ReferenceConfig<GreetingService> referenceConfig = new ReferenceConfig<>();
    
            // 2.设置应用程序信息
            referenceConfig.setApplication(new ApplicationConfig("dubbo-consumer"));
    
            // 3.设置服务注册中心
            referenceConfig.setRegistry(new RegistryConfig("ZKAddress"));
    
            // 4.设置服务接口和超时时间
            referenceConfig.setInterface(GreetingService.class);
            referenceConfig.setTimeout(5000);
    
            // 5.设置自定义负载均衡
            referenceConfig.setLoadbalance("myLoadBalance");
    
            // 6.设置服务分组与版本
            referenceConfig.setVersion("1.0.0");
            referenceConfig.setGroup("dubbo");
    
            // 7.引用服务
            GreetingService greetingService = referenceConfig.get();
            
        }
    
  • 相关阅读:
    idea打开了多个项目,多idea窗口相互切换的快捷键
    idea中的springboot项目如何不用重新编译,自动热部署
    线程基本使用--Thread内部方法调用start
    java如何快速创建List
    抓包工具Charles使用
    postman添加Cookie
    Ubuntu使用记录
    Intellij idea使用总结
    navicat 生成注册码( 仅供学习使用 )
    前端常用技术总结
  • 原文地址:https://www.cnblogs.com/gossip/p/14361927.html
Copyright © 2020-2023  润新知