Ribbon负载均衡服务调用

一、简介

​ 1、Ribbon 简介

​ Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具。主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon 客户端组件提供一系列完善的配置项如连接超时、重试等。简单的说就是在配置文件中列出Load Balancer 后面所有的机器,Ribbon 会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法(负载均衡+服务调用)。

​ github 地址 https://github.com/Netflix/ribbon ,现在项目处于维护状态中。

2、LB负载均衡(Load Balance)

  • 将用户的请求平摊到多个服务上,从而达到系统的高可用(HA)。常见的负载均衡软件有Nginx、LVS等。
  • Ribbon 本地负载均衡与Nginx 服务端负载均衡的区别
    • Nginx 是服务器负载均衡,客户端所有请求都会交给Nginx,然后由Nginx实现转发请求。即负载均衡由服务端实现。
    • Ribbon本地负载均衡在调用服务接口的时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。Ribbon属于进程内负载均衡(将LB逻辑集成到消费方,消费方从服务注册中心获知有那些地址可用,然后自己再从这些地址中选出一个合适的服务器)。

3、工作步骤

  • 选择Eureka Server,优先选择同一个区域内负载较少的Server;

  • 根据用户指定的策略(轮询、随机、根据响应时间加权),在server取到的服务注册列表中选择一个地址。

二、Ribbon负载均衡演示

1、在和Eureka集成的时候,只需要引入最新的Eureka的版本即可,因为Eureka以及集成了Ribbon。

<!-- eureka-client -->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

​ 然后结合RestTemplate使用即可。

三、Ribbon核心组件IRule

1、简介

​ 根据特定算法从服务列表中选取一个要访问的服务。接口类图如下:

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

2、负载规则配置

  • 自定义的规则配置不能放在 @ConponentScan 能扫描到的包及其子包,否则定义的配置类就会被所有的Ribbon客户端使用,起不到特殊化定制的效果。
  • 自定义配置类的编写
@SpringBootConfiguration
public class MySelfRule {

    @Bean
    public IRule myRule(){
        return new RandomRule(); //规则定义为随机
    }
}
  • 在主启动类上加上注解 @RibbonClient(value = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)。

四、Ribbon负载均衡算法

1、轮询原理

​ rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启后rest从1开始重新计数。

2、轮询源码

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

3、实现一个负载均衡算法(JUC+自旋锁)

  • 定义一个接口;
public interface LoadBalancer {

    /**
     *  获取执行当前请求的服务器
     * @param serviceInstances
     * @return
     */
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
  • 实现接口,需要添加一个 @Component;
@Component //让容器能够扫描到
public class MyLB implements LoadBalancer {
    //原子类,初始值0
    private AtomicInteger atomicInteger = new AtomicInteger(0);

    /**
     * 获得第几次访问的数
     * @return
     */
    public final int getAndIncrement(){
        int current;
        int next;
        //自旋锁
        do {
            current = this.atomicInteger.get();
            // 获取下一个请求次数,整型最大为2147483647
            next = current >= 2147483647 ? 0 : current + 1;
        }while (!this.atomicInteger.compareAndSet(current,next));
        return next;
    }
    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
        //访问到次数与集群服务器数量取模,获得该由哪个服务器执行该请求
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}
  • 服务调用
@Resource
private RestTemplate restTemplate;
@Resource
private LoadBalancer loadBalancer;//引入自定义的负载均衡算法
@Resource
private DiscoveryClient discoveryClient;
@GetMapping("/consumer/payment/lb")
public String getPaymentLB(){
    //获取服务集合
    List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
    if (instances == null || instances.size()<= 0){
        return null;
    }
    //获取执行当前请求的服务
    ServiceInstance serviceInstance = loadBalancer.instances(instances);
    URI uri = serviceInstance.getUri();
    return restTemplate.getForObject(uri+"/payment/lb",String.class);
}

案例代码地址:https://github.com/xhanglog/springcloud-learning

原文地址:https://www.cnblogs.com/Mhang/p/12555540.html