不懂Ribbon原理的可以进来看看哦,分析SpringBoot自动装配完成了Ribbon哪些核心操作

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 我们的Order服务作为服务提供者。创建SpringBoot项目,并添加相关依赖

image.png

  前面详细的给大家介绍了SpringBoot的核心内容,有了这部分的基础支持的话,我们再来分析SpringCloud中的相关组件就很容器了,本文我们来给大家开始介绍Ribbon的相关内容,首先来介绍下Ribbon项目在启动的时候完成了哪些操作。

一、项目案例准备

  首先我们大家案例环境,通过【RestTemplate】来实现服务调用,通过【Ribbon】实现客户端负载均衡操作。

image.png

1.Order服务

  我们的Order服务作为服务提供者。创建SpringBoot项目,并添加相关依赖

<?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.4.9</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.bobo.springcloud</groupId>
    <artifactId>spring-cloud-order-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-cloud-order-server</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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>Hoxton.SR10</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>

  然后在属性文件中添加相关的配置

spring.application.name=spring-cloud-order-service
server.port=8081

  然后创建自定义的Controller 提供对外的服务

@RestController
public class OrderController {
    @Value("${server.port}")
    private int port;
    @GetMapping("/orders")
    public String orders(){
        System.out.println("Order 服务端口是:"+port);
        return "Order Services ..... ";
    }
}

然后我们可以分别启动两个Order服务,端口分别设置为 8081和8082

2.User服务

  User服务作为调用用Order服务的客户端。也是我们要重点介绍【Ribbon】的服务。同样创建一个SpringBoot项目,添加相关的依赖

<?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.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.bobo.springcloud</groupId>
    <artifactId>spring-cloud-user-service2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-cloud-user-service2</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR10</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-ribbon</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>

然后在属性文件中配置相关信息

spring.application.name=spring-cloud-user-service
spring-cloud-order-service.ribbon.listOfServers=localhost:8081,localhost:8082

然后创建自定义的Controller来实现服务的调用

@RestController
public class UserController {
    @Autowired
    public RestTemplate restTemplate;
    @Autowired
    LoadBalancerClient loadBalancerClient;
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    @GetMapping("/users")
    public String users(){
        ServiceInstance choose = loadBalancerClient.choose("spring-cloud-order-service");
        String url = String.format("http://%s:%s",choose.getHost(),choose.getPort()+"/orders");
        //return restTemplate.getForObject(url,String.class);
        return restTemplate.getForObject("http://spring-cloud-order-service/orders",String.class);
    }
}

然后启动User服务访问,可以看到【Ribbon】默认通过轮询的方式来实现了服务的调用

image.png

二、Ribbon原理分析

  应用比较简单,我们主要是来分析下【Ribbon】的核心原理,先来看看自动装配做了哪些事情。

1.RibbonAutoConfiguration

  Ribbon在系统启动的时候自动装配完成的设置,我们先来看看对应的spring.factories 中的配置信息吧

image.png

emsp; 所以我们要继续来看【RibbonAutoConfiguration】配置类,我们贴出【RibbonAutoConfiguration】的关键信息

@Configuration
@Conditional({RibbonAutoConfiguration.RibbonClassesConditions.class})
@RibbonClients
@AutoConfigureAfter(
    name = {"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"}
)
// RibbonAutoConfiguration配置类注入容器后会完成 LoadBalancerAutoConfiguration 和 AsyncLoadBalancerAutoConfiguration 的注入
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {
    /**
    *  如果IoC容器中不存在 LoadBalancerClient 类型的对象就注入一个
    *  具体注入的类型为 RibbonLoadBalancerClient 对象
    **/
    @Bean
    @ConditionalOnMissingBean({LoadBalancerClient.class})
    public LoadBalancerClient loadBalancerClient() {
        return new RibbonLoadBalancerClient(this.springClientFactory());
    }
    // 省略其他代码

  通过源码查看我们知道在SpringBoot项目启动的时候完成了【LoadBalancerClient】对象的注入,且具体的类型为【RibbonLoadBalancerClient】,同时还会完成【LoadBalancerAutoConfiguration】这个配置类型的加载。在看【LoadBalancerAutoConfiguration】做了什么事情之前,我们先来搞清楚【@LoadBalanced】注解的作用

2.LoadBalancerAutoConfiguration

@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

  【@LoadBalanced】本质上就是一个【@Qualifier】注解。作用就是标记,我们通过案例来演示说明。

定义一个简单的【User】类

public class User {
    String name;
    public User(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

然后定义一个Java配置类,有两个添加了【@LoadBalanced】注解,有一个没有加。

@Configuration
public class JavaConfig {
    @LoadBalanced
    @Bean("user1")
    public User user1(){
        return new User("user1");
    }
    @Bean("user2")
    public User user2(){
        return new User("user2");
    }
    @LoadBalanced
    @Bean("user3")
    public User user3(){
        return new User("user3");
    }
}

然后创建我们的控制器,来测试使用

@RestController
public class UsersController {
    @LoadBalanced
    @Autowired
    List<User> list = Collections.emptyList();
    @GetMapping("/querys")
    public String query(){
        return list.toString();
    }
}

项目结构

image.png

启动SpringBoot项目后我们看效果

image.png

搞清楚了【@LoadBalanced】的作用后,我们再来看看【LoadBalancerAutoConfiguration】的配置加载做了什么事情

public class LoadBalancerAutoConfiguration {
    /**
    *   1.
    * 获取IoC容器中所有的被【@LoadBalanced】注解修饰的RestTemplate对象
    *  这些对象保存在了一个集合中
    **/
  @LoadBalanced
  @Autowired(required = false)
  private List<RestTemplate> restTemplates = Collections.emptyList();
  @Autowired(required = false)
  private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
    /**
    *   4.
    *  向容器中注入了 SmartInitializingSingleton 对象,并且实现了 SmartInitializingSingleton 接口中声明的
    *  afterSingletonsInstantiated 方法,在该方法中 通过3 中的 RestTemplateCustomizer中定义的 customize 方法
    *  实现了 RestTemplate 对象拦截器的植入
    **/
  @Bean
  public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
      final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
    return () -> restTemplateCustomizers.ifAvailable(customizers -> {
      for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
        for (RestTemplateCustomizer customizer : customizers) {
          customizer.customize(restTemplate);
        }
      }
    });
  }
  @Bean
  @ConditionalOnMissingBean
  public LoadBalancerRequestFactory loadBalancerRequestFactory(
      LoadBalancerClient loadBalancerClient) {
    return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
  }
  @Configuration(proxyBeanMethods = false)
  @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
  static class LoadBalancerInterceptorConfig {
        /**
        *  2. 
        *  创建了一个 LoadBalancerInterceptor 并注入到了容器中
        **/
    @Bean
    public LoadBalancerInterceptor loadBalancerInterceptor(
        LoadBalancerClient loadBalancerClient,
        LoadBalancerRequestFactory requestFactory) {
      return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
    }
    /**
        *  3. 
        *  创建了一个 RestTemplateCustomizer 并注入到了容器中
        *  而且通过内部类的方式定义定义了 RestTemplateCustomizer 接口中的 customize 方法的逻辑
        **/
    @Bean
    @ConditionalOnMissingBean
    public RestTemplateCustomizer restTemplateCustomizer(
        final LoadBalancerInterceptor loadBalancerInterceptor) {
      return restTemplate -> {
                // 获取 RestTemplate 中原有的 拦截器
        List<ClientHttpRequestInterceptor> list = new ArrayList<>(
            restTemplate.getInterceptors());
                // 在原有的拦截器的基础上 添加了一个 LoadBalancerInterceptor
        list.add(loadBalancerInterceptor);
                // 然后将添加有新的 拦截器的集合 设置到了 RestTemplate 对象中
        restTemplate.setInterceptors(list);
      };
    }
  }
   // 省略其他代码
}

通过对应的备注大家可以搞清楚该配置类的作用是实现了对【RestTemplate】对象(被@LoadBalanced修饰)植入

【LoadBalancerInterceptor】拦截器的功能。

小结Ribbon系统时的操作

image.png

~好了相信大家应该对于在自动装配时完成了 【RestTemplate】植入拦截器的逻辑应该很清楚了,下篇文章我们详细介绍Ribbon具体是怎么来处理负载均衡逻辑的,敬请期待,欢迎一键三连哦!!!


相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
1月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
460 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
182 2
|
4月前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
107 11
|
4月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue的高质量升学分析系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的高质量升学分析系统的详细设计和实现(源码+lw+部署文档+讲解等)
35 0
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的数据分析岗位招聘信息与分析附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的数据分析岗位招聘信息与分析附带文章源码部署视频讲解等
22 0
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的高校成绩分析附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的高校成绩分析附带文章源码部署视频讲解等
37 0
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的学生成绩分析和弱项辅助系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的学生成绩分析和弱项辅助系统附带文章源码部署视频讲解等
40 0
|
5月前
|
Java
SpringBoot起步依赖原理分析
SpringBoot起步依赖原理分析
|
5月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue的电商数据分析的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的电商数据分析的详细设计和实现(源码+lw+部署文档+讲解等)
52 0
|
6月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的自习室预订座位管理分析与实现的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的自习室预订座位管理分析与实现的详细设计和实现(源码+lw+部署文档+讲解等)
下一篇
无影云桌面