ribbon源码之负载均衡算法

IRule

  负载均衡器用来选择服务器的规则。

public interface IRule{
    public Server choose(Object key);
    public void setLoadBalancer(ILoadBalancer lb);
    public ILoadBalancer getLoadBalancer();
}

  通过BaseLoadBalancer的setRule或构造函数来为BaseLoadBalancer添加IRule

    public void setRule(IRule rule) {
        if (rule != null) {
            this.rule = rule;
        } else {
            /* default rule */
            this.rule = new RoundRobinRule();
        }
        if (this.rule.getLoadBalancer() != this) {
            this.rule.setLoadBalancer(this);
        }
    }

ribbon提供了部分路由规则。

RandomRule

  生成一个随机数,从负载均衡器中选取一个服务器。

public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;
        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();
            int serverCount = allList.size();
            if (serverCount == 0) {
                return null;
            }
            int index = rand.nextInt(serverCount);
            server = upList.get(index);
            if (server == null) {
                Thread.yield();
                continue;
            }
            if (server.isAlive()) {
                return (server);
            }
            server = null;
            Thread.yield();
        }
        return server;
    }

RoundRobinRule

  轮询从负载均衡器中选取一个服务器。

public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }
        Server server = null;
        int count = 0;
        while (server == null && count++ < 10) {
            List<Server> reachableServers = lb.getReachableServers();
            List<Server> allServers = lb.getAllServers();
            int upCount = reachableServers.size();
            int serverCount = allServers.size();

            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }
            int nextServerIndex = incrementAndGetModulo(serverCount);
            server = allServers.get(nextServerIndex);
            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }
            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }
            server = null;
        }
        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }

BestAvailableRule

  选择并发量最小且没有被熔断的服务器,需要使用到LoadBalancerStats来获取服务器的状态。

public Server choose(Object key) {
        if (loadBalancerStats == null) {
            return super.choose(key);
        }
        List<Server> serverList = getLoadBalancer().getAllServers();
        int minimalConcurrentConnections = Integer.MAX_VALUE;
        long currentTime = System.currentTimeMillis();
        Server chosen = null;
        for (Server server: serverList) {
            ServerStats serverStats = loadBalancerStats.getSingleServerStat(server);
            if (!serverStats.isCircuitBreakerTripped(currentTime)) {
                int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
                if (concurrentConnections < minimalConcurrentConnections) {
                    minimalConcurrentConnections = concurrentConnections;
                    chosen = server;
                }
            }
        }
        if (chosen == null) {
            return super.choose(key);
        } else {
            return chosen;
        }
    }

WeightedResponseTimeRule

  按照响应时间的比例来选择服务器。

  首先内部会有一个定时器,定时从负载均衡器里面读取服务器的平均响应时间,然后根据平均响应时间转换成权重。

class DynamicServerWeightTask extends TimerTask {
        public void run() {
            ServerWeight serverWeight = new ServerWeight();
            try {
                serverWeight.maintainWeights();
            } catch (Exception e) {
                logger.error("Error running DynamicServerWeightTask for {}", name, e);
            }
        }
    }
class ServerWeight {
        public void maintainWeights() {
            ILoadBalancer lb = getLoadBalancer();
            if (lb == null) {
                return;
            }
            if (!serverWeightAssignmentInProgress.compareAndSet(false,  true))  {
                return;
            }
            try {
                logger.info("Weight adjusting job started");
                AbstractLoadBalancer nlb = (AbstractLoadBalancer) lb;
                LoadBalancerStats stats = nlb.getLoadBalancerStats();
                if (stats == null) {
                    return;
                }
                double totalResponseTime = 0;
                // find maximal 95% response time
                for (Server server : nlb.getAllServers()) {
                    // this will automatically load the stats if not in cache
                    ServerStats ss = stats.getSingleServerStat(server);
                    totalResponseTime += ss.getResponseTimeAvg();
                }
                // weight for each server is (sum of responseTime of all servers - responseTime)
                // so that the longer the response time, the less the weight and the less likely to be chosen
                Double weightSoFar = 0.0;
                // create new list and hot swap the reference
                List<Double> finalWeights = new ArrayList<Double>();
                for (Server server : nlb.getAllServers()) {
                    ServerStats ss = stats.getSingleServerStat(server);
                    double weight = totalResponseTime - ss.getResponseTimeAvg();
                    weightSoFar += weight;
                    finalWeights.add(weightSoFar);
                }
                setWeights(finalWeights);
            } catch (Exception e) {
                logger.error("Error calculating server weights", e);
            } finally {
                serverWeightAssignmentInProgress.set(false);
            }

        }
    }

  然后根据权重来选择服务器

public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;
        while (server == null) {
            // get hold of the current reference in case it is changed from the other thread
            List<Double> currentWeights = accumulatedWeights;
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> allList = lb.getAllServers();
            int serverCount = allList.size();
            if (serverCount == 0) {
                return null;
            }
            int serverIndex = 0;
            // last one in the list is the sum of all weights
            double maxTotalWeight = currentWeights.size() == 0 ? 0 : currentWeights.get(currentWeights.size() - 1);
            // No server has been hit yet and total weight is not initialized
            // fallback to use round robin
            if (maxTotalWeight < 0.001d) {
                server =  super.choose(getLoadBalancer(), key);
                if(server == null) {
                    return server;
                }
            } else {
                // generate a random weight between 0 (inclusive) to maxTotalWeight (exclusive)
                double randomWeight = random.nextDouble() * maxTotalWeight;
                // pick the server index based on the randomIndex
                int n = 0;
                for (Double d : currentWeights) {
                    if (d >= randomWeight) {
                        serverIndex = n;
                        break;
                    } else {
                        n++;
                    }
                }
                server = allList.get(serverIndex);
            }

            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }

            if (server.isAlive()) {
                return (server);
            }

            // Next.
            server = null;
        }
        return server;
    }

AvailabilityFilteringRule

  使用RoundRobinRule来选择服务器,并且通过AvailabilityPredicate进行筛选。

ZoneAvoidanceRule

    

时间: 2024-11-13 07:49:02

ribbon源码之负载均衡算法的相关文章

Spring Cloud Ribbon 源码分析---负载均衡算法

上一篇分析了Ribbon如何发送出去一个自带负载均衡效果的HTTP请求,本节就重点分析各个算法都是如何实现. 负载均衡整体是从IRule进去的: public interface IRule{ /* * choose one alive server from lb.allServers or * lb.upServers according to key * * @return choosen Server object. NULL is returned if none * server i

Spring Cloud Ribbon源码分析---负载均衡实现

上一篇结合 Eureka 和 Ribbon 搭建了服务注册中心,利用Ribbon实现了可配置负载均衡的服务调用.这一篇我们来分析Ribbon实现负载均衡的过程. 从 @LoadBalanced入手 还记得前面配置 RestTemplate: @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } 在消费端使用Spring 提供的 RestTemplate 来发出请求,而Ribbon 在 Rest

【一起学源码-微服务】Ribbon 源码四:进一步探究Ribbon的IRule和IPing

前言 前情回顾 上一讲深入的讲解了Ribbon的初始化过程及Ribbon与Eureka的整合代码,与Eureka整合的类就是DiscoveryEnableNIWSServerList,同时在DynamicServerListLoadBalancer中会调用PollingServerListUpdater 进行定时更新Eureka注册表信息到BaseLoadBalancer中,默认30s调度一次. 本讲目录 我们知道Ribbon主要是由3个组件组成的: ILoadBalancer IRule IP

springCloud(7):Ribbon实现客户端侧负载均衡-消费者整合Ribbon

一.简介 Ribbon是Netfix发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为.为Ribbon配置服务提供者地址列表后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求.Ribbon默认为我们提供了很多的负载均衡算法,例如轮询.随机等,当然,也可以为Ribbon实现自定义的负载均衡算法. 在Spring Cloud中,当Ribbon与Eureka配合使用时,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提

spring-cloud-starter-ribbon提供客户端的软件负载均衡算法

Ribbon是什么? Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起.Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等.简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器.我们也很容易使用Ribbon实现自定义的负载均衡算法. LB方案分类 目前主流的LB方案可分成两类:一种是集中式LB, 即在服

# IT明星不是梦 #分布式负载均衡算法之亲和性轮询

无论是在早期的负载均衡器中,还是当前微服务基于客户端的负载均衡中,都有一个最基础的轮询算法,即将请求平均分布给多台机器,今天聊聊在此基础上, kube proxy是如何实现亲和性轮询的核心数据结构. 了解亲和性策略实现,失败重试等机制 1. 基础筑基 1.1 Service与Endpoints Service和Endpoint是kubernetes中的概念,其中Service代表一个服务,后面通常会对应一堆pod,因为pod的ip并不是固定的,用Servicel来提供后端一组pod的统一访问入口

Dubbo负载均衡算法

[图文并茂]一文讲透Dubbo负载均衡之最小活跃数算法  看源码思路 1.遍历 invokers 列表,寻找活跃数最小的 Invoker 2.如果有多个 Invoker 具有相同的最小活跃数,此时记录下这些 Invoker 在 invokers 集合中的下标,并累加它们的权重,比较它们的权重值是否相等 3.如果只有一个 Invoker 具有最小的活跃数,此时直接返回该 Invoker 即可 4.如果有多个 Invoker 具有最小活跃数,且它们的权重不相等,此时处理方式和 RandomLoadB

Citrix Netscaler负载均衡算法

众所周知,作为新一代应用交付产品的Citrix Netscaler具有业内领先的数据控制.应用交付的能力,然而作为根本内容之一的ADC功能,如果不具备强大的.多元化的均衡算法是不可能适应如此众多的应用场景,更无法做到好的应用交付产品.因此我们在此讨论一下比较常用的负载均衡算法就很有必要. 目前最新版本的Netscaler支持17种均衡算法,目前先讨论最常用的12种 1.轮询算法(Round Robin) 当NetScaler 使用轮询的负载均衡算法时,它会将来自客户端的请求轮流分配给后台中的服务

Spring Cloud 入门教程(五): Ribbon实现客户端的负载均衡

接上节,假如我们的Hello world服务的访问量剧增,用一个服务已经无法承载, 我们可以把Hello World服务做成一个集群. 很简单,我们只需要复制Hello world服务,同时将原来的端口8762修改为8763.然后启动这两个Spring Boot应用, 就可以得到两个Hello World服务.这两个Hello world都注册到了eureka服务中心.这时候再访问http://localhost:8761, 可以看到两个hello world服务已经注册.(服务与注册参见Spr