【Spring Cloud 四】Ribbon负载均衡

Ribbon负载均衡

系列文章目录

【Spring Cloud一】微服务基本知识
【Spring Cloud 三】Eureka服务注册与服务发现


背景

目前公司项目使用的注册中心主要是Spring Cloud Alibaba的Nacos做的注册中心和配置中心。并且Nacos使用了Ribbon作为默认的负载均衡器。但是相当于将Ribbon的负载均衡给透明化了,日常开发明面上是看不到Ribbon的。所以出于提升自己的角度,博主对Ribbon进行了理论学习并进行了实践。

一、什么是Ribbon

Spring Cloud Ribbon它是一个基于HTTP和TCP的客户端负载均衡工具,它是基于Netflix Ribbon实现的。通过Spring Cloud的封装,可以让我们轻松的面向服务的REST模板请求自动转换为客户端负载均衡的服务调用。

Ribbon主要干什么工作:它用在服务消费者需要调用多个相同功能的服务提供者实例时,帮助决定选择哪个服务提供者实例来完成调用。Ribbon可以通过多种负载均衡策略(随机、轮询、加权 、iphash)等方式,将请求分配到不同的服务实例上,以实现负载均衡和高可用。

二、为什么要有Ribbon

总体来说Ribbon是为了解决在微服务架构中服务嗲用的负载均衡和容错问题而引入的。

Ribbon解决了以下关键问题:

  1. 负载均衡:通过不同势力之间分配负载,Ribbon可以使系统更加稳定和高效,防止某个实例过载而导致服务不可用。
  2. 容错和自动恢复:如果调用的服务实例发生故障或不可用,Ribbon会自动尝试从其他健康的实例中选择一个进行请求处理,这种机制可以提高系统的可靠性和健壮性。
  3. 多种负载均衡策略:可以允许根据实际需求进行定制,更好的满足不同的应用场景。

三、使用Ribbon进行负载均衡

补充:Ribbon作为服务消费者的负载均衡器,有两种使用方式,一种是和RestTemplate相结合,另一种是和OpenFeign相结合。OpenFeign已经默认集成了Ribbon。

例系统中总共有四个服务,一个是Eureka来做服务注册和服务发现,两个服务提供者,一个服务消费者。

Eureka已经在这篇博客进行了实践【Spring Cloud 三】Eureka服务注册与服务发现,本篇博客不在进行一步一步搭建。

服务提供者A代码

pom文件

xml 复制代码
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.wangwei</groupId>
    <artifactId>provider-a</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>provider-a</name>
    <description>provider-a</description>
    <properties>
        <java.version>8</java.version>
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

yml配置文件

yaml 复制代码
server:
  port: 8080

spring:
    application:
      name: provider


eureka:
  client:
    service-url: 
      defaultZone: http://localhost:8761/eureka #eureka服务端和客户端的交互地址,不写的话默认是 8761,集群的话地址用,隔开。
    register-with-eureka: true #设置为fasle 不往eureka-server注册
    fetch-registry: true #应用是否拉取服务列表到本地
    registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答


  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: localhost #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔

启动类

java 复制代码
@SpringBootApplication
@EnableEurekaClient
public class ProviderAApplication {

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

}

controller

java 复制代码
@RestController
public class ProviderController {
    @GetMapping("hello")
    public String hello(){
        return "我是服务提供者A";
    }
}

服务提供者B

pom文件

xml 复制代码
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.wangwei</groupId>
    <artifactId>provider-b</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>provider-b</name>
    <description>provider-b</description>
    <properties>
        <java.version>8</java.version>
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

yml配置文件

yaml 复制代码
server:
  port: 8081

spring:
  application:
    name: provider


eureka:
  client:
    service-url: 
      defaultZone: http://localhost:8761/eureka
    register-with-eureka: true #设置为fasle 不往eureka-server注册
    fetch-registry: true #应用是否拉取服务列表到本地
    registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答


  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: localhost #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔

启动类

java 复制代码
@SpringBootApplication
@EnableEurekaClient
public class ProviderBApplication {

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

}

controller

java 复制代码
@RestController
public class ProviderController {
    @GetMapping("hello")
    public String hello(){
        return "我是服务提供者B";
    }
}

服务消费者

pom文件

xml 复制代码
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.wangwei</groupId>
    <artifactId>consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>consumer</name>
    <description>consumer</description>
    <properties>
        <java.version>8</java.version>
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.2.9.RELEASE</version>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

yml文件

yaml 复制代码
server:
  port: 8082

spring:
  application:
    name: consumer


eureka:
  client:
    service-url: 
      defaultZone: http://localhost:8761/eureka
    register-with-eureka: true #设置为fasle 不往eureka-server注册
    fetch-registry: true #应用是否拉取服务列表到本地
    registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答


  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: localhost #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔



ribbon:
    eager-load:
        enabled: false #ribbon只有自己的话是不能做服务发现,需要借助eureka。如果为false表示懒加载,请求调用的时候开启。如果为true表示程序启动的时候开启
    eureka:
        enabled: true
    http: #我们使用ribbon用的是RestTemplate发请求。底层是java.net.HttpUrlConnection发的请求, 很方便但是不支持连接池
        client:  # 发送请求的工具有很多如:httpClient 它支持连接池,效率更好。如果你想该请求的工具,需要添加对应的依赖。
          enabled: false
    okhttp:   #这个也是请求工具,移动端用的比较多, 轻量级请求
        enabled: false

启动类

java 复制代码
@SpringBootApplication
@EnableEurekaClient
public class ConsumerApplication {

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

    /**
     * 这个RestTemplate
     * 添加上了LoadBalanced 它会被ribbon来操作
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
 }

controller

java 复制代码
@RestController
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("testRibbon")
    public String testRibbon(String serviceName){
        String result = restTemplate.getForObject("http://" + serviceName + "/hello", String.class);
        return result;
    }
}

运行测试

先运行Eureka服务,再运行服务提供者最后运行服务消费者。

调用接口的实现效果:Ribbon默认的负载均衡策略是轮询,及A------B------A------B。

四、Ribbon的负载均衡策略

Ribbon核心接口

根据负载均衡策略从所有对应的服务中选取一个服务

Ribbon提供的负载均衡算法

  1. com.netflix.loadbalancer.RoundRobinRule 轮询
  2. com.netflix.loadbalancer.RandomRule 随机
  3. com.netflix.loadbalancer.WeightedResponseTimeRule 权重轮询
  4. com.netflix.loadbalancer.AvailabilityFilteringRule 会先过滤掉由于多次访问故障处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对于剩余的服务按照轮询的策略进行访问。
  5. com.netflix.loadbalancer.WeightedResponseTimeRule 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越大。刚启动时如果统计信息不足,则使用轮询的策略,等统计信息足够会切换到自身规则。
  6. com.netflix.loadbalancer.BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量小的服务。
  7. com.netflix.loadbalancer.RetryRule 先按照轮询的策略获取服务,如果获取服务失败则在指定的时间内会进行重试,获取可用服务。

配置此消费者嗲用任何服务都用某种负载均衡策略

示例:这里设置的是随机策略 ,这样的话这个消费者调用所有的服务负载均衡策略都是随机策略。

启动类中添加,当然你也可以是新建一个类并添加上@Configuration注解。

java 复制代码
    /**
     * @description:往容器中方一个rule对象
     * 你访问任何一个提供者 都是这个算法
     * @author: wangwei
     * @date: 2023/7/31 8:49
     * @param: []
     * @return: com.netflix.loadbalancer.IRule
     **/
    @Bean
    public IRule myRule(){
        return new RandomRule();
    }

}

访问不同的服务使用不同的算法规则

在yml文件中添加:

yaml 复制代码
#访问不同的服务可以使用不同的算法规则
provider: #服务的应用名
  ribfatkunbon:
      NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #算法的全限定类名

示例:

yaml 复制代码
server:
  port: 8082

spring:
  application:
    name: consumer


eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
    register-with-eureka: true #设置为fasle 不往eureka-server注册
    fetch-registry: true #应用是否拉取服务列表到本地
    registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答


  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: localhost #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔

#访问不同的服务可以使用不同的算法规则
provider: #服务的应用名
  ribfatkunbon:
      NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #算法的全限定类名

ribbon:
    eager-load:
        enabled: false #ribbon只有自己的话是不能做服务发现,需要借助eureka。如果为false表示懒加载,请求调用的时候开启。如果为true表示程序启动的时候开启
    eureka:
        enabled: true
    http: #我们使用ribbon用的是RestTemplate发请求。底层是java.net.HttpUrlConnection发的请求, 很方便但是不支持连接池
        client:  # 发送请求的工具有很多如:httpClient 它支持连接池,效率更好。如果你想该请求的工具,需要添加对应的依赖。
          enabled: false
    okhttp:   #这个也是请求工具,移动端用的比较多, 轻量级请求
        enabled: false

总结

  1. 通过对Ribbon的理论和实践的学习,让我对Ribbon的理解不再停留知道的层面上,而是切切实实的实践过。
  2. 相对整体和系统的对Ribbon进行了了解和学习,并且和其他微服务组件进行了链接。
相关推荐
牛奶咖啡1312 小时前
云计算核心技术之云网络技术
云计算·负载均衡·云网络技术·专有/私有网络vpc·vpc系统架构·云网络关键技术·容器云网络技术
sniper_fandc17 小时前
Spring Cloud系列—SkyWalking告警和飞书接入
spring cloud·skywalking
努力买辣条1 天前
基于Docker的高可用WordPress集群部署:Nginx负载均衡+Mysql主从复制+ProxySQL读写分离
nginx·docker·负载均衡
abigalexy1 天前
深入图解Spring Cloud底层设计
spring·spring cloud
楠有枝2 天前
普通用户使用docker命令
spring cloud·docker·eureka
孤狼程序员3 天前
【Spring Cloud 微服务】2.守护神网关Gateway
spring cloud·微服务·gateway
朱皮皮呀3 天前
Spring Cloud——服务注册与服务发现原理与实现
运维·spring cloud·eureka·服务发现·php
百思可瑞教育3 天前
Spring Cloud Gateway 负载均衡全面指南
运维·负载均衡·北京百思可瑞教育·百思可瑞教育·北京百思教育
朱皮皮呀4 天前
微服务流量分发核心:Spring Cloud 负载均衡解析
spring cloud·微服务·负载均衡
源码宝5 天前
【智慧工地源码】智慧工地云平台系统,涵盖安全、质量、环境、人员和设备五大管理模块,实现实时监控、智能预警和数据分析。
java·大数据·spring cloud·数据分析·源码·智慧工地·云平台