手把手帶你分析SpringBoot自動(dòng)裝配完成了Ribbon哪些核心操作
一、項(xiàng)目案例準(zhǔn)備
首先我們大家案例環(huán)境,通過【RestTemplate】來實(shí)現(xiàn)服務(wù)調(diào)用,通過【Ribbon】實(shí)現(xiàn)客戶端負(fù)載均衡操作。
1.Order服務(wù)
我們的Order服務(wù)作為服務(wù)提供者。創(chuàng)建SpringBoot項(xiàng)目,并添加相關(guān)依賴
<?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>
然后在屬性文件中添加相關(guān)的配置
spring.application.name=spring-cloud-order-service server.port=8081
然后創(chuàng)建自定義的Controller 提供對(duì)外的服務(wù)
@RestController public class OrderController { @Value("${server.port}") private int port; @GetMapping("/orders") public String orders(){ System.out.println("Order 服務(wù)端口是:"+port); return "Order Services ..... "; } }
然后我們可以分別啟動(dòng)兩個(gè)Order服務(wù),端口分別設(shè)置為 8081和8082
2.User服務(wù)
User服務(wù)作為調(diào)用用Order服務(wù)的客戶端。也是我們要重點(diǎn)介紹【Ribbon】的服務(wù)。同樣創(chuàng)建一個(gè)SpringBoot項(xiàng)目,添加相關(guān)的依賴
<?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>
然后在屬性文件中配置相關(guān)信息
spring.application.name=spring-cloud-user-service spring-cloud-order-service.ribbon.listOfServers=localhost:8081,localhost:8082
然后創(chuàng)建自定義的Controller來實(shí)現(xiàn)服務(wù)的調(diào)用
@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); } }
然后啟動(dòng)User服務(wù)訪問,可以看到【Ribbon】默認(rèn)通過輪詢的方式來實(shí)現(xiàn)了服務(wù)的調(diào)用
二、Ribbon原理分析
應(yīng)用比較簡(jiǎn)單,我們主要是來分析下【Ribbon】的核心原理,先來看看自動(dòng)裝配做了哪些事情。
1.RibbonAutoConfiguration
Ribbon在系統(tǒng)啟動(dòng)的時(shí)候自動(dòng)裝配完成的設(shè)置,我們先來看看對(duì)應(yīng)的spring.factories 中的配置信息吧
emsp; 所以我們要繼續(xù)來看【RibbonAutoConfiguration】配置類,我們貼出【RibbonAutoConfiguration】的關(guān)鍵信息
@Configuration @Conditional({RibbonAutoConfiguration.RibbonClassesConditions.class}) @RibbonClients @AutoConfigureAfter( name = {"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"} ) // RibbonAutoConfiguration配置類注入容器后會(huì)完成 LoadBalancerAutoConfiguration 和 AsyncLoadBalancerAutoConfiguration 的注入 @AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class}) @EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class}) public class RibbonAutoConfiguration { /** * 如果IoC容器中不存在 LoadBalancerClient 類型的對(duì)象就注入一個(gè) * 具體注入的類型為 RibbonLoadBalancerClient 對(duì)象 **/ @Bean @ConditionalOnMissingBean({LoadBalancerClient.class}) public LoadBalancerClient loadBalancerClient() { return new RibbonLoadBalancerClient(this.springClientFactory()); } // 省略其他代碼
通過源碼查看我們知道在SpringBoot項(xiàng)目啟動(dòng)的時(shí)候完成了【LoadBalancerClient】對(duì)象的注入,且具體的類型為【RibbonLoadBalancerClient】,同時(shí)還會(huì)完成【LoadBalancerAutoConfiguration】這個(gè)配置類型的加載。在看【LoadBalancerAutoConfiguration】做了什么事情之前,我們先來搞清楚【@LoadBalanced】注解的作用
2.LoadBalancerAutoConfiguration
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Qualifier public @interface LoadBalanced { }
【@LoadBalanced】本質(zhì)上就是一個(gè)【@Qualifier】注解。作用就是標(biāo)記,我們通過案例來演示說明。
定義一個(gè)簡(jiǎn)單的【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 + '\'' + '}'; } }
然后定義一個(gè)Java配置類,有兩個(gè)添加了【@LoadBalanced】注解,有一個(gè)沒有加。
@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"); } }
然后創(chuàng)建我們的控制器,來測(cè)試使用
@RestController public class UsersController { @LoadBalanced @Autowired List<User> list = Collections.emptyList(); @GetMapping("/querys") public String query(){ return list.toString(); } }
項(xiàng)目結(jié)構(gòu)
啟動(dòng)SpringBoot項(xiàng)目后我們看效果
搞清楚了【@LoadBalanced】的作用后,我們?cè)賮砜纯础綥oadBalancerAutoConfiguration】的配置加載做了什么事情
public class LoadBalancerAutoConfiguration { /** * 1. * 獲取IoC容器中所有的被【@LoadBalanced】注解修飾的RestTemplate對(duì)象 * 這些對(duì)象保存在了一個(gè)集合中 **/ @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); @Autowired(required = false) private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList(); /** * 4. * 向容器中注入了 SmartInitializingSingleton 對(duì)象,并且實(shí)現(xiàn)了 SmartInitializingSingleton 接口中聲明的 * afterSingletonsInstantiated 方法,在該方法中 通過3 中的 RestTemplateCustomizer中定義的 customize 方法 * 實(shí)現(xiàn)了 RestTemplate 對(duì)象攔截器的植入 **/ @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. * 創(chuàng)建了一個(gè) LoadBalancerInterceptor 并注入到了容器中 **/ @Bean public LoadBalancerInterceptor loadBalancerInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) { return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); } /** * 3. * 創(chuàng)建了一個(gè) RestTemplateCustomizer 并注入到了容器中 * 而且通過內(nèi)部類的方式定義定義了 RestTemplateCustomizer 接口中的 customize 方法的邏輯 **/ @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { // 獲取 RestTemplate 中原有的 攔截器 List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); // 在原有的攔截器的基礎(chǔ)上 添加了一個(gè) LoadBalancerInterceptor list.add(loadBalancerInterceptor); // 然后將添加有新的 攔截器的集合 設(shè)置到了 RestTemplate 對(duì)象中 restTemplate.setInterceptors(list); }; } } // 省略其他代碼 }
通過對(duì)應(yīng)的備注大家可以搞清楚該配置類的作用是實(shí)現(xiàn)了對(duì)【RestTemplate】對(duì)象(被@LoadBalanced修飾)植入【LoadBalancerInterceptor】攔截器的功能。
總結(jié)
Ribbon系統(tǒng)時(shí)的操作
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
springmvc實(shí)現(xiàn)簡(jiǎn)單的攔截器
這篇文章主要為大家詳細(xì)介紹了springmvc實(shí)現(xiàn)簡(jiǎn)單攔截器的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05解決java編譯錯(cuò)誤( 程序包javax.servlet不存在javax.servlet.*)
這篇文章主要介紹了解決java編譯錯(cuò)誤的相關(guān)資料,主要解決 程序包javax.servlet不存在javax.servlet.*的問題,需要的朋友可以參考下2017-08-08啟動(dòng)SpringBoot報(bào)JavaMail加載錯(cuò)誤的原因分析和解決
這篇文章給大家介紹了啟動(dòng)SpringBoot報(bào)JavaMail加載錯(cuò)誤的原因分析和解決,文中通過代碼示例給出了詳細(xì)的原因分析和解決方法,對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-01-01Java 常見異常(Runtime Exception )詳細(xì)介紹并總結(jié)
這篇文章主要介紹了Java 常見異常(Runtime Exception )詳細(xì)介紹并相關(guān)資料,大家在開發(fā)Java 應(yīng)用軟件的時(shí)候經(jīng)常會(huì)遇到各種異常這里幫大家整理了一部分,并解釋如何解決,需要的朋友可以參考下2016-10-10Java SSM實(shí)現(xiàn)前后端協(xié)議聯(lián)調(diào)詳解上篇
首先我們已經(jīng)知道,在現(xiàn)在流行的“前后端完全分離”架構(gòu)中,前后端聯(lián)調(diào)是一個(gè)不可能避免的問題,這篇文章主要介紹了Java SSM實(shí)現(xiàn)前后端協(xié)議聯(lián)調(diào)過程2022-08-08GitLab+Jenkins+Maven+Tomcat?實(shí)現(xiàn)自動(dòng)集成、打包、部署
本文主要介紹了GitLab?+?Jenkins?+?Maven?+?Tomcat?實(shí)現(xiàn)自動(dòng)集成、打包、部署,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01