手把手带你分析SpringBoot自动装配完成了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系统时的操作
本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注脚本之家的更多内容!