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

一、项目案例准备

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

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】默认通过轮询的方式来实现了服务的调用

二、Ribbon原理分析

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

1.RibbonAutoConfiguration

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



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();
}
}

项目结构

启动SpringBoot项目后我们看效果

搞清楚了【@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系统时的操作

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

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

  1. 不懂Ribbon原理的可以进来看看哦,分析RibbonClientConfiguration完成了哪些核心初始操作

      本文在前一篇文章的基础上来继续分析Ribbon的核心内容. 不懂Ribbon原理的可以进来看看哦,分析SpringBoot自动装配完成了Ribbon哪些核心操作 RibbonClientConfi ...

  2. SpringBoot启动流程分析(五):SpringBoot自动装配原理实现

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  3. SpringBoot自动装配原理解析

    本文包含:SpringBoot的自动配置原理及如何自定义SpringBootStar等 我们知道,在使用SpringBoot的时候,我们只需要如下方式即可直接启动一个Web程序: @SpringBoo ...

  4. springboot自动装配原理

    最近开始学习spring源码,看各种文章的时候看到了springboot自动装配实现原理.用自己的话简单概括下. 首先打开一个基本的springboot项目,点进去@SpringBootApplica ...

  5. 小BUG大原理 | 第一篇:重写WebMvcConfigurationSupport后SpringBoot自动配置失效

    一.背景 公司的项目前段时间发版上线后,测试反馈用户的批量删除功能报错.正常情况下看起来应该是个小BUG,可怪就怪在上个版本正常,且此次发版未涉及用户功能的改动.因为这个看似小BUG我了解到不少未知的 ...

  6. springboot自动装配原理,写一个自己的start

    springboot自动装配原理 第一次使用springboot的时候,都感觉很神奇.只要加入一个maven的依赖,写几行配置,就能注入redisTemple,rabbitmqTemple等对象. 这 ...

  7. SpringBoot | 2.1 SpringBoot自动装配原理

    @ 目录 前言 1. 引入配置文件与配置绑定 @ImportResource @ConfigurationProperties 1.1 @ConfigurationProperties + @Enab ...

  8. SpringBoot 自动装配原理

    早期的Spring项目需要添加需要配置繁琐的xml,比如MVC.事务.数据库连接等繁琐的配置.Spring Boot的出现就无需这些繁琐的配置,因为Spring Boot基于约定大于配置的理念,在项目 ...

  9. springboot自动装配原理回顾、配置文件分析

    配置文件 spring boot官方文档 官方外部配置文件说明参考文档 自动配置原理分析 1. SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfigurat ...

随机推荐

  1. Maven:手动添加jar包进Maven本地库内

    正常maven依赖jar包的pom.xml写法如下: <!-- https://mvnrepository.com/artifact/ojdbc/ojdbc --><!-- (参数一 ...

  2. 查看python的安装版本,位数及安装路径

    一.想要查看ubuntu中安装的Python路径 方法一:whereis python (用来快速查找任何文件,是一个文件搜索命令,与locate的功能一样.执行whereis python 会将所有 ...

  3. bugku本地包含

    重点:eval()函数有执行漏洞 函数本身作用,把字符串当成php代码计算. 所以自然想到,可以把我们的写好的php代码写入进去. 题目又暗示在本地,想到flag.php了,所以想办法把文件里面的内容 ...

  4. F5的IPv6配置指导

    1.配置核心思想: 配置IPv6的默认路由 配置IPv6的VS IPv6的vs里面要启用"automap" 2.配置IPv6的默认路由 3.配置IPv6的VS 第一种方法: 第二种 ...

  5. Linux sudo命令——sudoers文件的配置

    Linux sudo命令与其配置文件/etc/sudoers   对linux有一定了解的人多少也会知道点关于sudo命令.sudo命令核心思想是权限的赋予 ,即某个命令的所属用户不是你自己,而你却有 ...

  6. ctf之SusCTF2017-Crack Zip

    题目信息如下,可知为杂项题,且无提示 下载文件打开如图,该压缩包是加密的 首先想到的是暴力破解,下载zip暴力破解软件打开文件. 下一步,选择暴力破解 进行暴力破解设定,进行破解 破解完成,得到密解压 ...

  7. 「 题解」NOIP2021模拟赛(2021-07-19)

    小兔的话 欢迎大家在评论区留言哦~ D - 矩阵 简单题意 一个 \(i * i\) 的 \(01\) 矩阵,若满足 每一行 和 每一列 都满足 恰好 有 \(2\) 个位置是 \(1\) 时,称为 ...

  8. SpringBoot缓存管理(三) 自定义Redis缓存序列化机制

    前言 在上一篇文章中,我们完成了SpringBoot整合Redis进行数据缓存管理的工作,但缓存管理的实体类数据使用的是JDK序列化方式(如下图所示),不便于使用可视化管理工具进行查看和管理. 接下来 ...

  9. Hive——join的使用

    Hive--join的使用 hive中常用的join有:inner join.left join .right join .full join.left semi join.cross join.mu ...

  10. python内置函数dir()

    描述 dir() 函数不带参数时,返回当前范围内的变量.方法和定义的类型列表:带参数时,返回参数的属性.方法列表.如果参数包含方法__dir__(),该方法将被调用.如果参数不包含__dir__(), ...