Ribbon负载均衡

一、概述

负载均衡Ribbon

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

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

目前Ribbon项目的状态处于:维护中。

复制代码
	https://github.com/Netflix/ribbon/wiki

LoadBalance负载均衡

复制代码
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA【高可用】

常见的负载均衡有软件Nginx,LVS,硬件 F5等。

Ribbon与Nginx负载均衡的区别

复制代码
Nginx是服务器负载均衡,客户端所有请求都会交给nginx,nginx实现转发请求,负载均衡由服务端实现。

Ribbon是本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用。

Ribbon负载均衡演示

Ribbon工作步骤

复制代码
选择EurekaServer,优先选择在同一区域内负载较少的server。
根据用户指定的策略,从server获取到的服务注册列表中选择一个地址。策略包括:轮询,随机,根据响应时间加权。

二、Ribbon负载均衡策略

复制代码
第一种:随机策略(RandomRule),自然是随性而为,随机选择服务名称对应的服务节点。
第二种:轮询策略(RoundRobinRule),一个一个来,谁不用急,底层使用了CAS+自旋锁的方式来保证线程安全。
第三种:重试策略(RetryRule),一次不行再来第二次,使用了类似于装饰器设计模式,相当于重试+实际模式的方式,所以需要指定真正被执行的负载均衡策略。
第四种:权重策略(WeightedResponseTimeRule),谁响应快,那么谁被选中的几率就高,该规则会自动根据服务节点的响应时间来计算权重,响应快则权重高。该Rule继承自RoundRobinRule,所以服务器刚启动时采用轮询策略,当权重数据积累足够后才使用WeightedResponseTimeRule模式。
第五种:空闲策略(BestAvailableRule),谁最闲谁先来,在过滤掉故障服务后,选择过去30分钟类并发量最小的服务节点。当然服务器刚启动时,统计结果未生成时,依然使用轮询的方式。
第六种:下限策略(AvailabilityFilteringRule),基于RoundRobinRule来选择服务节点,但必须满足它的最低要求,否在不予采纳,10次以内选择种为止。
第七种:自主策略(ZoneAvoidanceRule),以机房大区(Zone)和可用性作为过滤条件,选择可用的服务节点。满足大区要求,且服务可用且并发量不大的节点才会被选中。

三、源码解析-LoadBalancer底层机制&可扩展性

1.通过源码解读7中负载均衡策略

RandomRule

实现关键:

RoundRobinRule

实现关键:

BestAvailableRule

实现关键:

父类是基于RoundRobinRule来实现的

RetryRule

实现关键:

RetryRule只是在底层实际策略上加了一层重试机制,下图的"answer"就是实际的策略,默认使用RoundRobinRule,但是可以替换的

2.了解ribbon中的自旋锁(cas)

无限循环 + 退出条件

java 复制代码
for (;;) {
    int current = nextServerCyclicCounter.get();
    int next = (current + 1) % modulo;
    if (nextServerCyclicCounter.compareAndSet(current, next))
        return next;
}

对于 nextServerCyclicCounter.compareAndSet(current, next) 解释;

这表示我当前拿到的这个current变量在内存中没有被改变过,则可以设置为+1后的值

官方解释:

1.取得当前值

2.计算+1后的值

3.如果当前值还有效(没有被)的话设置那个+1后的值

4.如果设置没成功(当前值已经无效了即被别的线程改过了), 再从1开始.

3.编程好习惯之防御性编程

假定所有的输入项都是不安全的,对所有不安全的可能加以判断

例如:RandomRule中的参数判断和线程状态判断

四、负载均衡器LoadBalancer原理解析

Ribbon中一共用7中负载均衡策略,那么我们在调用的过程中,由谁来调用这些具体的策略呢?

**答案:**关键就在于Ribbon对RestTemplate的改造。

关键: @LoadBalancer注解

由@LoadBalancer注解标记RestTemplate,然后Ribbon将带有负载均衡功能的拦截器注入标记好的RestTemplate中,以此来实现负载均衡。

5.IPing机制

IPing机制是一种主动机制,他能主动判断当前服务器节点的状态,并决定该节点是否可作为目标节点,只用可用的节点才会作为负载均衡的目标节点。

三种机制:

Dummy:默认返回true,即认为所有节点都是可用的

NIWSDiscoveryPing:借助Eureka服务发现机制获取节点状态,节点状态为UP则认为节点可用

PingUrl: 主动向服务节点发起HTTP调用,如节点响应,才认为节点可用。

在与Eureka组件搭配使用时,默认使用NIWSDiscoveryPing机制,以此减少服务节点负担。

五、整合Ribbon

我们要整合Ribbon,当然需要引入Ribbon响应的依赖:

java 复制代码
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

spring-cloud-starter-netflix-eureka-clien包整合了ribbon依赖
org.springframework.cloud spring-cloud-starter-netflix-ribbon 2.2.6.RELEASE compile

这就是为什么我们不用显式地去引入Ribbon的依赖,我们也可以知道Ribbon的实现其实就是:负载均衡+RestTemplate调用

1.创建 cloud-consumer-order80

2.pom

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2024</artifactId>
        <groupId>com.my.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-order80</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.my.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

3.新建负载均衡配置类

java 复制代码
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @auther
 * @desc 负载均衡配置类
 */
@Configuration
public class LoadConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

4.启动类:

java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class OrderConsumer80 {

    public static void main(String[] args) {
        SpringApplication.run(OrderConsumer80.class, args);
    }

}

5.用于测试的Controller

java 复制代码
import com.my.springcloud.entities.Payment;
import com.my.springcloud.lb.LoadBalancer;
import com.my.springcloud.utils.RestResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.net.URI;
import java.util.List;

/**
 * @auther
 * @desc 负责均衡测试
 */
@RestController
@Slf4j
public class OrderController {

    //public static final String PAYMENT_URL = "http://localhost:8001";

    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @Resource
    private RestTemplate restTemplate;


    @GetMapping("/consumer/payment/create")
    public RestResponse<Payment> create(Payment payment) {
        return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, RestResponse.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public RestResponse<Payment> getPayment(@PathVariable("id") Long id) {
        return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, RestResponse.class);
    }


}

5.测试负载均衡

先启动EurekaServer:7001和7002服务

再启动服务提供者 cloud-provider-payment8001 和8002

再启动服务消费者 cloud-consumer-order80

查看服务注册情况

访问测试

访问 http://localhost/consumer/payment/get/1

不断发起调用检查是否具有负载均衡效果

Ribbon默认使用的是RoundRobinRule(轮询策略)

六、Ribbon如何更改负载规则

我们需要在@ComponentScan扫描不到的包下定义配置类,否则该配置类就会被所有Ribbon客户端所共享,因而达不到定制的效果。

1.新建package

复制代码
	在java目录下新建 com.my.myrule包

包结构如下:

2.新建规则配置类 MyLoadConfig

java 复制代码
package com.my.myrule;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @auther
 * @create 更改负载均衡策略
 */
@Configuration
public class MyLoadConfig {
    @Bean
    public IRule myRule() {
        return new RandomRule();//定义为随机
    }
}

3.主启动类上添加@RibbonClient 注解

@RibbonClient: 启用自定义ribbon负载均衡策略, name代表访问哪个微服务,configuration配置负载均衡算法

java 复制代码
package com.my.springcloud;


import com.my.myrule.MyLoadConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

@SpringBootApplication
@EnableEurekaClient
//@RibbonClient: 启用自定义ribbon负载均衡策略,name代表访问哪个微服务,configuration配置负载均衡算法
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MyLoadConfig.class)
public class OrderConsumer80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderConsumer80.class, args);
    }

}

4、测试更改后的负载均衡策略

复制代码
访问    http://localhost/consumer/payment/get/1

再次点击出现随机分配,代表启用成功

相关推荐
echoyu.4 小时前
消息队列-初识kafka
java·分布式·后端·spring cloud·中间件·架构·kafka
AAA修煤气灶刘哥5 小时前
缓存这「加速神器」从入门到填坑,看完再也不被产品怼慢
java·redis·spring cloud
AAA修煤气灶刘哥6 小时前
接口又被冲崩了?Sentinel 这 4 种限流算法,帮你守住后端『流量安全阀』
后端·算法·spring cloud
✎﹏赤子·墨筱晗♪7 小时前
从反向代理到负载均衡:Nginx + Tomcat 构建高可用Web服务架构
nginx·tomcat·负载均衡
叶绪2587 小时前
Nginx 反向代理 + Tomcat 集群:负载均衡配置步骤与核心原理
nginx·tomcat·负载均衡
T_Ghost10 小时前
SpringCloud微服务服务容错机制Sentinel熔断器
spring cloud·微服务·sentinel
喂完待续12 小时前
【序列晋升】28 云原生时代的消息驱动架构 Spring Cloud Stream的未来可能性
spring cloud·微服务·云原生·重构·架构·big data·序列晋升
荣光波比13 小时前
Nginx 实战系列(四)—— Nginx反向代理与负载均衡实战指南
运维·nginx·云计算·负载均衡
惜.己1 天前
Docker启动失败 Failed to start Docker Application Container Engine.
spring cloud·docker·eureka