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

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

相关推荐
.生产的驴1 分钟前
SpringCloud OpenFeign用户转发在请求头中添加用户信息 微服务内部调用
spring boot·后端·spring·spring cloud·微服务·架构
一元咖啡8 小时前
SpringCloud Gateway转发请求到同一个服务的不同端口
spring·spring cloud·gateway
天天扭码9 小时前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
跳跳的向阳花10 小时前
03-03、SpringCloud第三章,负载均衡Ribbon和Feign
spring cloud·ribbon·负载均衡
天天扭码1 天前
五天SpringCloud计划——DAY1之mybatis-plus的使用
java·spring cloud·mybatis
luckywuxn1 天前
Spring Cloud Alibaba、Spring Cloud 与 Spring Boot各版本的对应关系
spring boot·spring·spring cloud
linweidong1 天前
MariaDB面试题及参考答案
linux·运维·数据库·负载均衡·dba·mariadb·后端面试
wclass-zhengge2 天前
SpringCloud篇(服务网关 - GateWay)
spring boot·spring cloud·gateway
荆州克莱2 天前
Redis | Redis常用命令及示例总结(API)
spring boot·spring·spring cloud·css3·技术