项目代码GitHub地址:SpringCloud入门学习

何为负载均衡:通俗点讲就是将一次请求分摊到多个操作单元上。在分布式系统架构中,为了提高系统的可用性,通常会有很多相同的服务提供者,那每一次请求该如何选择哪一个服务呢?这就是负载均衡存在的意义,明确如何选择。
Spring Cloud Ribbon:是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。,它不像服务注册中心、配置中心、API网关那样需要独立部署,它默认存在于每一个Spring Cloud构建的微服务和基础设施中。

  1. 其实在使用Eureka的时候,你就已经不经意间使用了Ribbon,引入Eureka依赖就会自动引入Ribbon的依赖
    在这里插入图片描述
    当然你也可以再单独引入

    	<!-- 其实上面引入eureka的时候已经自动引入了ribbon相关依赖 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>      
            </dependency>
    
  2. 在你使用标注了@LoadBalancedRestTemplate调用服务的时候,就是用了Ribbon,开启了负载均衡
    Ribbon默认使用的是RoundRobinRule轮询算法(每个服务按顺序请求一次)
    Ribbon自带负载均衡策略:
    BestAvailableRule :选择一个最小的并发请求的server
    AvailabilityFilteringRule :过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值)
    WeightedResponseTimeRule :根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低
    RetryRule :对选定的负载均衡策略机上重试机制
    RoundRobinRule:roundRobin方式轮询选择server
    RandomRule:随机选择一个server
    ZoneAvoidanceRule:复合判断server所在区域的性能和server的可用性选择server

  3. 切换Ribbon默认的负载均衡算法

    	/**
         * 使用随机负载均衡算法
         */
        @Bean
        public RandomRule randomRule() {
            return new RandomRule();
        }
    

    加入到Ioc容器中即可

  4. 自定义负载均衡算法
    要求:每个服务轮询访问3次
    对照着RoundRobinRule源码,进行了改写,代码如下

    package com.fei.springcloudconsumer.config;
    
    import com.fei.springcloudconsumer.config.log.Loggable;
    import com.netflix.client.config.IClientConfig;
    import com.netflix.loadbalancer.AbstractLoadBalancerRule;
    import com.netflix.loadbalancer.ILoadBalancer;
    import com.netflix.loadbalancer.Server;
    
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * @Author: xiaoshijiu
     * @Date: 2019/7/2
     * @Description: 自定义负载均衡算法,每个服务轮询3次
     */
    public class MyRule extends AbstractLoadBalancerRule implements Loggable {
    
        private AtomicInteger nextServerCyclicCounter;
        // 定义轮询次数
        private AtomicInteger totle;
    
        public MyRule() {
            nextServerCyclicCounter = new AtomicInteger(0);
            totle = new AtomicInteger(0);
        }
    
        public MyRule(ILoadBalancer lb) {
            this();
            setLoadBalancer(lb);
        }
    
        public Server choose(ILoadBalancer lb, Object key) {
            if (lb == null) {
                getLog().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)) {
                    getLog().warn("No up servers available from load balancer: " + lb);
                    return null;
                }
    
                int nextServerIndex = myincrementAndGetModulo(serverCount);
                server = allServers.get(nextServerIndex);
    
                if (server == null) {
                    /* Transient. */
                    Thread.yield();
                    continue;
                }
    
                if (server.isAlive() && (server.isReadyToServe())) {
                    return (server);
                }
    
                // Next.
                server = null;
            }
    
            if (count >= 10) {
                getLog().warn("No available alive servers after 10 tries from load balancer: " + lb);
            }
            return server;
        }
    
        /**
         * 具体实现算法。可能写的不好,实现效果
         */
        private int myincrementAndGetModulo(int modulo) {
            for (;;) {
                int current = nextServerCyclicCounter.get();
                int next = (current + 1) % modulo;
                // 预期一样的话就把value更新为next
                if (totle.get() < 3 && nextServerCyclicCounter.compareAndSet(current, current)) {
                    // 获取并自增
                    totle.getAndIncrement();
                    if (totle.get() == 3) {
                        // 重新赋值
                        totle.getAndSet(0);
                        nextServerCyclicCounter.getAndSet(next);
                    }
                    return next;
                }
            }
        }
    
        @Override
        public Server choose(Object key) {
            return choose(getLoadBalancer(), key);
        }
    
        @Override
        public void initWithNiwsConfig(IClientConfig clientConfig) {
        }
    }
    

    RoundRobinRule源码一样,使用AtomicInteger原子类

  5. 将自定义的算法MyRule加入到IOC容器中

    	/**
         * 自定义负载均衡算法,每个服务轮询3次
         */
        @Bean
        public MyRule myRule() {
            return new MyRule();
        }
    
  6. 官方文档指出:

    这个自定义的类不能放在@ComponentScan所扫描的当前包以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon 客户端所共享,也就是我们达不到特殊化指定的目的了。

  7. 可以使用@RibbonClient(name = "SPRINGCLOUD-EMPLOYEE-PROVIDER", configuration = MyRule.class)明确指定针对哪个服务进行负载均衡,而configuration指定负载均衡的算法具体实现类。
    当然也可以没有,即对所有服务生效。

Logo

开源、云原生的融合云平台

更多推荐