【SpringCloud】五、Ribbon

2022-06-28 20:56:07 浏览数 (1)

Ribbon

简介

  • Spring Cloud Ribbon也是基于Netflix Ribbon实现的一套客户端负载均衡和服务调用的工具。可配置连接超时、重试的机制,实现自定义负载均衡算法。
  • GitHub Ribbon已进入维护模式,未来可能会被Spring Cloud Loadbalancer替代。
  • Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
  • 底层使用RestTemplate
  • 提供的负载均衡算法有:轮询(默认),随机,根据响应时间加权

架构

Ribbon在工作时分两步

  1. 优先选择负载最小的注册中心
  2. 根据用户配置的负载均衡算法,再从注册中心获取的服务注册表中选择一个地址

负载规则

  • RoundRobinRule 轮询(默认规则)
  • RandomRule 随机
  • RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重
  • WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
  • BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
  • AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
  • ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择服务器

RoundRobinRule源码分析

代码语言:javascript复制
public class RoundRobinRule extends AbstractLoadBalancerRule {

    private AtomicInteger nextServerCyclicCounter;
    private static final boolean AVAILABLE_ONLY_SERVERS = true;
    private static final boolean ALL_SERVERS = false;

    private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);

    public RoundRobinRule() {
        nextServerCyclicCounter = new AtomicInteger(0);
    }

    public RoundRobinRule(ILoadBalancer lb) {
        this();
        setLoadBalancer(lb);
    }

    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);
            }

            // Next.
            server = null;
        }

        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                      lb);
        }
        return server;
    }

    /**
     * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
     *
     * @param modulo The modulo to bound the value of the counter.
     * @return The next value.
     */
    private int incrementAndGetModulo(int modulo) {
        for (;;) {
            int current = nextServerCyclicCounter.get();
            int next = (current   1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next))
                return next;
        }
    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}
  • choose(ILoadBalancer lb, Object key) 选择服务节点
  • incrementAndGetModulo(int modulo)获取下一个服务节点
    • 用上一个服务节点的下标 1后对所有服务节点数量取余,整个过程用到了CAS
    • 并没有采用:记录总访问次数,每次对所有服务节点数量取余。可避免精度溢出,轮询算法可参照这里。

替换默认负载算法

RibbonCustomRule 自定义负载算法的配置类
代码语言:javascript复制
@Configuration
@ExcludeComponentScan
public class RibbonCustomRule {

    @Bean
    public IRule iRule() {
        return new RandomRule();
    }
}
OrderMain1080 启动类
代码语言:javascript复制
@EnableDiscoveryClient
@EnableEurekaClient
@SpringBootApplication
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = ExcludeComponentScan.class)})
@RibbonClient(name = OrderController.PAYMENT_SERVER, configuration = RibbonCustomRule.class)
public class OrderMain1080 {
    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(OrderMain1080.class, args);
    }
}
  • @ExcludeComponentScan防止被@ComponentScan扫描到,否则这个配置类就会被所有的Ribbon客户端所共享。
  • OrderController.PAYMENT_SERVER = "CLOUD-PAYMENT-SERVICE"服务在注册中心里的名称

Post Views: 149

0 人点赞