• 12Ribbon


    1. 概述

    Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端-负载均衡的工具。

    简单地说,Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon 客户端组件提供一系列完善的配置项如连接超时、重试等。

    Ribbon 目前也进入维护模式,未来的替换方案是 Spring Cloud Starter Loadbalancer。

    • 进程内 LB
      • 将 LB 逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器;
      • Ribbon 就属于进程内 LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址(在调用微服务接口的时候,会在注册中心上获取注册信息服务列表之后缓存到 JVM 本地,从而在本地实现 RPC 远程服务调用技术);
    • 集中式 LB
      • 在服务的消费方和提供方之间使用独立的 LB 设备(可能是硬件,如 F5;也可能是软件,如 Nginx),由该设备负责把访问请求通过某种策略转发至服务的提供方;

    2. 说明

    Ribbon 架构说明:

    Ribbon 在工作时分为两步:

    1. 先选择 EurekaServer,它优先选择在同一个区域内负载较少的 Server;
    2. 再根据用户指定的策略,在从 Server 取到的服务注册列表中选择一个地址;

    Eureka 已经依赖了 Ribbon:

    RestTemplate 说明:

    如果需要使用 ip:port 就不需要加 @LoadBalanced 注解,如果需要使用 application.name 访问,那就需要在 restTemplate 配置加上 @LoadBalanced,因为 @LoadBalanced 这个注解是负载均衡的注解,而负载均衡就是通过访问服务名而实现的。如果你加上这个注解之后使用 restTemplate,那么他就默认你的 localhost 一个模块的服务名称("ip:port"),而不是本机的 IP。总而言之,如果需要使用 ip:port,就不需要加 @LoadBalanced;如果需要使用应用名访问,那就需要在 restTemplate 配置加上 @LoadBalanced

    3. 核心组件 IRule

    根据特定算法从服务列表中选取一个要访问的服务。

    【测试:当调用指定微服务时使用自定义的 Rule】

    1. 自定义类放置在主启动类所在包同级的包下(不能放在 @ComponentScan 所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的 Ribbon 客户端所共享,达不到特殊化定制的目的了);
      @Configuration
      public class MyRibbonConfig {
          @Bean
          public IRule myRule(){
              return new RandomRule();
          }
      }
      
    2. 修改主启动类
      @EnableEurekaClient
      @SpringBootApplication
      @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MyRibbonConfig.class)
      public class OrderMain8080 {
          public static void main(String[] args) {
              SpringApplication.run(OrderMain8080.class, args);
          }
      }
      

    4. 负载均衡算法

    RoundRobinRule 源码如下:

    /*
     *
     * Copyright 2013 Netflix, Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    package com.netflix.loadbalancer;
    
    import com.netflix.client.config.IClientConfig;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * The most well known and basic load balancing strategy, i.e. Round Robin Rule.
     *
     * @author stonse
     * @author Nikos Michalakis <nikos@netflix.com>
     *
     */
    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) {
        }
    }
    

    手写一个

    1. 8001/8002 controller 新增方法:
      @GetMapping("/lb")
      public String testLoadBalance() {
          return port;
      }
      
    2. 去掉 @LoadBalanced 注解
      @Configuration
      public class ApplicationContextConfig {
          /**
           * 如果需要使用ip和端口,就不需要加 @LoadBalanced
           * 如果需要使用应用名访问,那就需要加上 @LoadBalanced
           * @return
           */
          @Bean
          // @LoadBalanced
          public RestTemplate restTemplate() {
              return new RestTemplate();
          }
      }
      
    3. 8080 服务新增接口 MyLoadBalance
      public interface MyLoadBalance {
      
          /**
           * 返回下一个要调用的 ServiceInstance
           * @param serviceInstances 收集服务器总共有多少台能够提供服务的机器,并放到list里面
           * @return
           */
          ServiceInstance getNextServer(List<ServiceInstance> serviceInstances);
      }
      
    4. 8080 服务新增接口实现
      @Component
      public class MyLoadBalanceImpl implements MyLoadBalance {
      
          private AtomicInteger nextServerIndex = new AtomicInteger(0);
      
          private int getNextServerIndex(int serverCount) {
              int current, next;
              do {
                  current = this.nextServerIndex.get();
                  next = (current+1) % serverCount;
              } while (this.nextServerIndex.compareAndSet(current, next));
              return this.nextServerIndex.get();
          }
      
          @Override
          public ServiceInstance getNextServer(List<ServiceInstance> serviceInstances) {
              return serviceInstances.get(getNextServerIndex(serviceInstances.size()));
          }
      }
      
    5. 8080 服务 controller 新增测试方法
      @Resource
      private MyLoadBalance myLoadBalance;
      
      @Resource
      private DiscoveryClient discoveryClient;
      
      @GetMapping("/payment/lb")
      public CommonResult<String> testLoadBalance() {
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        if (instances == null || instances.isEmpty()) {
          return new CommonResult(HttpStatus.SERVICE_UNAVAILABLE.value(), "无可用服务", null);
        }
        ServiceInstance nextServer = myLoadBalance.getNextServer(instances);
        log.info("LB -> {}", nextServer.getUri());
        return new CommonResult(HttpStatus.OK.value(), "负载均衡",
                    restTemplate.getForObject(nextServer.getUri()+"/payment/lb", String.class));
      }
      
  • 相关阅读:
    PDE_DATA 定义
    每天写日记争创青年艺术家 2014-5-14
    [Servlet3.0新功能]注释替代配置文件
    Flex4+Spring3+Hibernate3+BlazeDS整合笔记
    Flex博客
    hibernate缓存机制详细分析
    计算机的艺术-算法
    算法/数据结构/数学
    线性表 及Java实现 顺序表、链表、栈、队列
    java 图的邻接矩阵
  • 原文地址:https://www.cnblogs.com/liujiaqi1101/p/16126487.html
Copyright © 2020-2023  润新知