• Robbin负载均衡


    Robbin是在Spring Cloud中的一个组件,是由Netfix发布的负载均衡器,有助于控制HTTP和TCP客户端的行为。它给我们提供了默认的轮询、随机等负载均衡算法。同时也可以由我们定义自己的算法。

    由于Robbin已经被集成在Eureka里面,因此我们这个样例的代码都是在《微服务Eureka使用详解》的基础上进行。

    参考博客:https://blog.csdn.net/u013089490/article/details/83786844https://blog.csdn.net/dwhdome/article/details/86477961 

    负载均衡样例

    (1)我们首先启动好在《微服务Eureka使用详解》中编写的三个服务:服务注册中心,user服务,roles服务。访问Eureka的管理页面可以看到如下内容:

    (2)下面我们先来修改User服务(只修改controller):

        @GetMapping("users/{id}")
        public String getUser(@PathVariable("id") String id) {
            String str = "7001User.id" + id;
            System.out.println(str);
            return str;
        }

    启动服务,它的端口是7001。

    然后再复制一个User项目,将Controller内容调整为如下:

        @GetMapping("users/{id}")
        public String getUser(@PathVariable("id") String id) {
            String str = "7002User.id" + id;
            System.out.println(str);
            return str;
        }

    以及将配置文件中的端口修改为7002

    server.port=7002

    启动该应用。

    这时我们再查看Eureka服务页面:

    可以清楚的看到USER服务的可用区域(Availability Zones)已经从(1)变成了(2)。状态(status)已经变成了两个服务地址7001和7002。

    (3)Roles服务的负载均衡在《微服务Eureka使用详解》中已经配置过了,我们这里查看一下即可。

        @Bean
        @LoadBalanced
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }

    (4)访问Roles服务路径~/roles/{id},可用连续多次访问,这里假如我连续访问5次:

    /roles/1
    /roles/2
    /roles/3
    /roles/4
    /roles/5

    可用看到返回的结果:

    7001User.id1
    7002User.id2
    7001User.id3
    7002User.id4
    7001User.id5

    两个User服务,7001端口和7002端口是默认处于一个轮询的状态。假设这一次访问7001端口,下一次就访问7002端口,以此类推。

    修改负载均衡策略

    负责负载均衡策略的顶级接口:

    com.netflix.loadbalancer.IRule

    所有的负责均衡算法均实现了这个接口,它的实现类如下:

    默认情况下,使用的是

    com.netflix.loadbalancer.RoundRobinRule:以轮询的方式进行负载均衡。

    常用的还有

    com.netflix.loadbalancer.RandomRule:随机策略
    com.netflix.loadbalancer.RetryRule:重试策略。
    com.netflix.loadbalancer.WeightedResponseTimeRule:权重策略。会计算每个服务的权重,越高的被调用的可能性越大。
    com.netflix.loadbalancer.BestAvailableRule:最佳策略。遍历所有的服务实例,过滤掉故障实例,并返回请求数最小的实例返回。
    com.netflix.loadbalancer.AvailabilityFilteringRule:可用过滤策略。过滤掉故障和请求数超过阈值的服务实例,再从剩下的实力中轮询调用。

     如果我们要实现自己的策略,可以继承IRule接口,下面我们来以RoundRobinRule为例查看一下如何实现负载均衡策略。

    (1)IRule接口

    public interface IRule {
    
        // 返回经过负载均衡后最终调用的服务
        Server choose(Object var1);
    
        void setLoadBalancer(ILoadBalancer var1);
    
        ILoadBalancer getLoadBalancer();
    }

    (2)RoundRobinRule类

    我们先看最重要的choose(Object)方法

        public Server choose(Object key) {
            return this.choose(this.getLoadBalancer(), key);
        }

    里面调用了我们另一个choose(ILoadBalancer, Object)方法

        public Server choose(ILoadBalancer lb, Object key) {
            if (lb == null) {
                log.warn("no load balancer");
                return null;
            } else {
                Server server = null;
                int count = 0;
    
                while(true) {
                    if (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) {
                            int nextServerIndex = this.incrementAndGetModulo(serverCount);
                            server = (Server)allServers.get(nextServerIndex);
                            if (server == null) {
                                Thread.yield();
                            } else {
                                if (server.isAlive() && server.isReadyToServe()) {
                                    return server;
                                }
    
                                server = null;
                            }
                            continue;
                        }
    
                        log.warn("No up servers available from load balancer: " + lb);
                        return null;
                    }
    
                    if (count >= 10) {
                        log.warn("No available alive servers after 10 tries from load balancer: " + lb);
                    }
    
                    return server;
                }
            }
        }

    在上面的方法中,主要内容是在while(true)内获取下一个server,获取的方法是incrementAndGetModulo(int)。然后根据方法返回的服务下标,从服务集合中找到对应的server,如果server存在且存活,会直接使用这个server。如果server不存在或不存在,则会再循环获取下一个。直到循环10次,或着没有从服务注册中心找到可用的服务,会返回null。

     核心的incrementAndGetModulo(int)方法

        private int incrementAndGetModulo(int modulo) {
            int current;
            int next;
            do {
                current = this.nextServerCyclicCounter.get(); //nextServerCyclicCounter是AtomicInteger对象,默认值0,可保证线程安全性 
                next = (current + 1) % modulo; //每次往后移一位,取集合中的下一个server。这里要注意的是从1开始,即数组中的第二个server会被第一个调用。
            } while(!this.nextServerCyclicCounter.compareAndSet(current, next)); //操作完成后用CAS操作将next赋值给nextServerCyclicCounter
    
            return next;
        }

    (3)可用顺便再看一下RetryRule类。

        public Server choose(ILoadBalancer lb, Object key) {
            long requestTime = System.currentTimeMillis();
            long deadline = requestTime + this.maxRetryMillis;
            Server answer = null;
            answer = this.subRule.choose(key); //内部仍然是使用了轮询策略。
            if ((answer == null || !answer.isAlive()) && System.currentTimeMillis() < deadline) {
                InterruptTask task = new InterruptTask(deadline - System.currentTimeMillis());
    
                while(!Thread.interrupted()) {
                    answer = this.subRule.choose(key);
                    if (answer != null && answer.isAlive() || System.currentTimeMillis() >= deadline) {
                        break;
                    }
    
                    Thread.yield();
                }
    
                task.cancel();
            }
    
            return answer != null && answer.isAlive() ? answer : null;
        }

    这个类采用的是重试策略,可以看到里面其实仍是采用了轮询策略,只不过如果轮询的server无法访问,或者不存活,会在指定的时间(500)内一直获取下一个server,直到找到一个存活的server。

    注意:上面所说的故障服务,是由Eureka注册中心来判断。即使服务已经挂掉,但是Eureka的实例未过期,仍会被判断为正常。但是实际的返回可能是null等。

    如果我们要实现自己的负载均衡策略,也可以通过继承IRule接口,在配置文件中进行配置。 

  • 相关阅读:
    positio:absolute与position:relative的区别
    angular过滤器
    docker常用命令
    Promise的用法
    import和require
    webStrom支持Vue
    搭建Vue2+Vuex+Webpack+Pug(jade)+Stylus环境
    需要转义的正则表达式符号
    改变input光标颜色与输入字体颜色不同
    网络编程套接字,osi七层架构各层协议最全讲解
  • 原文地址:https://www.cnblogs.com/yxth/p/10913295.html
Copyright © 2020-2023  润新知