欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Cloud Ribbon负载均衡服务调用

程序员文章站 2022-06-22 16:17:00
...

概述

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

Ribbon官网:https://github.com/Netflix/ribbon维护模式

负载均衡的表现就是,将用户的请求分摊到多个服务器上,从而达到高可用的目的。常见的负载均衡软件有:Nginx、LVS等。

Nginx是服务器端负载均衡,客户端的请求都发给Nginx,由Nginx实现分发,将请求发送到不同的服务器上。

Ribbon是客户端负载均衡,在调用微服务接口的时候,会在注册中心拿到注册信息服务列表缓存到本地JVM,在客户端通过某种规则,确定请求的链接,发送请求进行调用。

使用Ribbon负载均衡

其实在spring-cloud-starter-netflix-eureka-client坐标下,是引入了spring-cloud-starter-netflix-ribbon的,所以,只需要添加一个@LoadBalanced就可以实现负载均衡。

RestTemplate常见的方法有getForObject()、getForEntity()、postForObject()、postForEntity()方法。其中*ForObject()方法返回对象为响应体中数据转换成的对象,基本理解为JSON。*ForEntity()方法返回对象是ResponseEntity对象,包含了响应中的信息,比如响应头,响应状态码,响应体等。

新建两个模块(cloud-eureka-server7001、cloud-eureka-server7002),使用eureka实现服务注册,具体可看之前笔记,,将cloud-eureka-server7001、cloud-eureka-server7002的配置文件改成集群模式,让7001和7002互相注册,新建两个模块(cloud-provider-payment8001、cloud-provider-payment8002),也就是服务提供者,将cloud-provider-payment8001、cloud-provider-payment8002的配置文件改成集群模式,将cloud-consumer-order80的配置文件改成集群模式。新建一个服务消费者模块(cloud-consumer-order80),在cloud-consumer-order80模块中的OrderController里,添加两个方法,分别调用getForEntity()和postForEntity()方法。

注:新建模块过程省略,步骤可参考之前笔记

@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult getPaymentById2(@PathVariable("id") Long id) {
    ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
    System.out.println("status code=" + entity.getStatusCode());
    System.out.println("headers=" + entity.getHeaders());
    if (entity.getStatusCode().is2xxSuccessful()) {
        return entity.getBody();
    } else {
        return new CommonResult(404, "查找失败");
    }
}
@GetMapping("/consumer/payment/create2")
public CommonResult create2(Payment payment) {
    return restTemplate.postForEntity(PAYMENT_URL + "/payment/create", payment, CommonResult.class).getBody();
}

先启动Eureka注册中心,再启动两个生产者模块,最后启动消费者模块,浏览器发送请求来调用ForEntity()方法进行测试。在浏览器端,可以看到port的值,不断在8001和8002之前进行切换。

Ribbon负载均衡算法

负载均衡算法:REST接口第几次请求数字%服务器集群总数量=实际调用服务器位置下标,每次重启服务器REST接口计数从1开始。找一个IRule接口的实现类,下面是RoundRobinRule类对该接口的实现。

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) {
            // 获取server,如果获取失败,进行重试,如果10次后,还没获取到server,就报错
            if (server == null && count++ < 10) {
                // 获取所有状态是up的server
                List<Server> reachableServers = lb.getReachableServers();
                // 获取所有server
                List<Server> allServers = lb.getAllServers();
                int upCount = reachableServers.size();
                int serverCount = allServers.size();
                if (upCount != 0 && serverCount != 0) {
                    // 计算出要访问的server下标
                    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;
        }
    }
}
 
private int incrementAndGetModulo(int modulo) {
    int current;
    int next;
    // 利用cas和自旋锁,获取next的值
    do {
        current = this.nextServerCyclicCounter.get();
        next = (current + 1) % modulo;
    } while(!this.nextServerCyclicCounter.compareAndSet(current, next));
    return next;
}

从上面可以发现,Ribbon核心是IRule,IRule是一个接口,它有多个实现类,分别代表不同的负载均衡策略。

IRule的子类的关系图

Spring Cloud Ribbon负载均衡服务调用

注:

com.netflix.loadbalancer.RoundRobinRule:轮询机制

com.netflix.loadbalancer.RandomRule:随机机制

com.netflix.loadbalancer.RetryRule:首先按照RoundRobinRule策略来获取服务,如获取服务失败,在指定时间内尝试获取服务,获取可用服务

com.netflix.loadbalancer.WeigthResponseTimeRule:是对RoundRobinRule扩展,根据响应速度进行选择,响应速度越快,优先级越高

com.netflix.loadbalancer.BestAvailableRule:首先剔除掉多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

com.netflix.loadbalancer.AvailabilityFilterRule:首先过滤掉故障实例,然后再选择并发最小的实例

com.netflix.loadbalancer.ZoneAvoidanceRule:默认规则,符合判断server所在区域的性能和server可用性选择服务器