javascript
SpringCloud Nacos + Ribbon 调用服务的 2 种方法!
作者 | 磊哥
來源 | Java中文社群(ID:javacn666)
轉載請聯系授權(微信ID:GG_Stone)
在 Nacos 中,服務調用主要是通過 RestTemplate + Ribbon 實現的,RestTemplate 是 Spring 提供的 Restful 請求實現類,而 Ribbon 是客戶端負載均衡器,通過 Ribbon 可以獲取服務實例的具體信息(IP 和端口號),之后再通過 RestTemplate 加服務實例的具體信息就可以完成一次服務調用了。
而 RestTemplate + Ribbon 調用服務的實現方式兩種:通過代碼的方式調用服務和通過注解方式調用服務。但兩種實現方式的原理都是一樣的:都是通過注冊中心,將可用服務列表拉取到本地(客戶端),再通過客戶端負載均衡器得到某個服務器的具體信息,然后請求此服務器即可,如下圖所示:
1.代碼方式調用
通過代碼的方式調用服務在實際工作中并不常用,主要是寫法太麻煩,但了解它對于后面理解注解調用方式有很大的幫助,所以我們這里重點來看一下。服務調用需要有兩個角色:一個是服務提供者(Provider),另一個是服務調用者(Consumer),接下來我們來創建一下這兩個角色。
1.1 創建服務提供者:Provider
第一步:先創建一個 Spring Boot 項目(Spring Cloud 項目是基于 Spring Boot 創建的),添加 spring-web 和 nacos-discovery 依賴,具體依賴信息如下:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency> <!--?添加?Nacos?支持?--> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>第二步:設置 Nacos 相關配置,在 application.yml 中添加以下配置:
spring:application:name:?springcloud-nacos-provider?#?項目名稱(nacos?注冊的服務名)cloud:nacos:discovery:username:?nacos?#?nacos?登錄用戶名password:?nacos666?#?nacos?密碼server-addr:?127.0.0.1:8848?#?nacos?服務端地址 server:port:?8081?#?項目啟動端口號第三步:添加服務方法,如下代碼所示:
import?org.springframework.boot.SpringApplication; import?org.springframework.boot.autoconfigure.SpringBootApplication; import?org.springframework.web.bind.annotation.PathVariable; import?org.springframework.web.bind.annotation.RequestMapping; import?org.springframework.web.bind.annotation.RestController;@SpringBootApplication @RestController public?class?HttpProviderApplication?{public?static?void?main(String[]?args)?{SpringApplication.run(HttpProviderApplication.class,?args);}/***?為客戶端提供可調用的接口*/@RequestMapping("/call/{name}")public?String?call(@PathVariable?String?name)?{return?"I'm?Provider.?Received?a?message?from:?"?+?name;} }然后使用相同的方法再創建 2 個服務提供者,最終對應的端口號分別為:
127.0.0.1:8081 127.0.0.1:8082 127.0.0.1:8083
這 3 個服務提供者分別打印的內容是“I'm Provider...”、“I'm Provider2...”、“I'm Provider3...”,如下圖所示:
1.2 創建服務調用者:Consumer
本文的核心是服務調用者的實現代碼,它的創建方式和服務提供者的創建方式類似。第一步:創建一個 Spring Boot 項目,添加 spring-web 和 nacos-discovery 依賴,具體依賴內容如下:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency> <!--?添加?Nacos?支持?--> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>可能有人會有疑問,本文標題是 Spring Cloud Alibaba Nacos + Ribbon,那為什么不添加 Ribbon 的依賴呢?這是因為 Spring Cloud Alibaba Nacos 中已經內置了 Ribbon 框架了,打開項目的依賴樹就可以清楚的看到了,如下圖所示:第二步:設置 Nacos 相關配置,在 application.yml 中添加以下配置:
spring:application:name:?springcloud-nacos-consumer?#?項目名稱(nacos?注冊的服務名)cloud:nacos:discovery:username:?nacos?#?nacos?登錄用戶名password:?nacos666?#?nacos?密碼server-addr:?127.0.0.1:8848?#?nacos?服務端地址 server:port:?8091?#?項目啟動端口號第三步:在項目啟動類中,使用 Spring Java Config 的方式聲明 RestTemplate 對象,如下代碼所示:
import?org.springframework.boot.SpringApplication; import?org.springframework.boot.autoconfigure.SpringBootApplication; import?org.springframework.context.annotation.Bean; import?org.springframework.web.client.RestTemplate;@SpringBootApplication public?class?RibbonCodeConsumerApplication?{public?static?void?main(String[]?args)?{SpringApplication.run(RibbonCodeConsumerApplication.class,?args);}/***?使用?Spring?Java?Config?方式聲明?RestTemplate*/@BeanRestTemplate?restTemplate()?{return?new?RestTemplate();} }第四步:使用 RestTemplate + Ribbon 的代碼方式調用服務,首先使用 Ribbon 提供的 LoadBalancerClient 對象的 choose 方法,根據 Nacos 中的服務 id 獲取某個健康的服務實例,服務實例中包含服務的 IP 地址和端口號,然后再使用 RestTemplate 根據獲取到的 IP 和 端口號訪問服務即可,具體實現代碼如下:
import?org.springframework.cloud.client.ServiceInstance; import?org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import?org.springframework.web.bind.annotation.GetMapping; import?org.springframework.web.bind.annotation.RequestParam; import?org.springframework.web.bind.annotation.RestController; import?org.springframework.web.client.RestTemplate; import?javax.annotation.Resource;@RestController public?class?ConsumerController?{//?Ribbon?提供的負載均衡對象@Resourceprivate?LoadBalancerClient?loadBalancerClient;//?Spring?提供進行?Restful?請求對象@Resourceprivate?RestTemplate?restTemplate;@GetMapping("/consumer")public?String?consumer(@RequestParam?String?name)?{//?根據?Ribbon?提供的對象?+?Nacos?的服務?id?獲取服務實例ServiceInstance?serviceInstance?=?loadBalancerClient.choose("springcloud-nacos-provider");//?獲取服務實例中的?ipString?ip?=?serviceInstance.getHost();//?獲取服務實例中的端口號int?port?=?serviceInstance.getPort();//?使用?restTemplate?請求并獲取結果String?result?=?restTemplate.getForObject("http://"?+?ip?+?":"?+?port?+?"/call/"?+?name,String.class);return?result;} }以上程序的執行結果如下圖所示:
2.注解方式調用
使用注解方式調用服務就簡單多了,服務提供者的創建方法和上面相同,這里就不再贅述了,接下來我們來創建一個注解方式的服務調用者 Consumer。第一步:創建一個 Spring Boot 項目,添加 spring-web 和 nacos-discovery 依賴,具體依賴內容如下:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency> <!--?添加?Nacos?支持?--> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>第二步:設置 Nacos 相關配置,在 application.yml 中添加以下配置:
spring:application:name:?springcloud-nacos-consumer?#?項目名稱(nacos?注冊的服務名)cloud:nacos:discovery:username:?nacos?#?nacos?登錄用戶名password:?nacos666?#?nacos?密碼server-addr:?127.0.0.1:8848?#?nacos?服務端地址 server:port:?8092?#?項目啟動端口號第三步:在項目啟動類中,使用 Spring Java Config 的方式聲明 RestTemplate 對象,此步驟中,需要在 RestTemplate 對象上加上 @LoadBalanced 注解,加上此注解之后就可以讓 RestTemplate 對象自動支持負載均衡了,如下代碼所示:
import?org.springframework.boot.SpringApplication; import?org.springframework.boot.autoconfigure.SpringBootApplication; import?org.springframework.cloud.client.loadbalancer.LoadBalanced; import?org.springframework.context.annotation.Bean; import?org.springframework.web.client.RestTemplate;@SpringBootApplication public?class?RibbonAnnotationConsumerApplication?{public?static?void?main(String[]?args)?{SpringApplication.run(RibbonAnnotationConsumerApplication.class,?args);}@LoadBalanced?//?使?RestTemplate?自動支持?Ribbon?負載均衡@Beanpublic?RestTemplate?restTemplate()?{return?new?RestTemplate();} }第四步:創建客戶端請求方法,具體實現代碼如下:
import?org.springframework.web.bind.annotation.GetMapping; import?org.springframework.web.bind.annotation.RequestParam; import?org.springframework.web.bind.annotation.RestController; import?org.springframework.web.client.RestTemplate;import?javax.annotation.Resource;@RestController public?class?ConsumerController?{@Resourceprivate?RestTemplate?restTemplate;@GetMapping("/consumer")public?String?consumer(@RequestParam?String?name)?{//?請求并獲取結果(springcloud-nacos-provider?為?Nacos?服務id)String?result?=?restTemplate.getForObject("http://springcloud-nacos-provider/call/"?+?name,?String.class);return?result;} }以上程序的執行結果如下圖所示:
注解實現原理分析
通過上述代碼我們可以看出,Nacos 實現調用服務的關鍵是通過 @LoadBalanced,它為 RestTemplate 賦予了負載均衡的能力,從而可以正確的調用到服務,那 @LoadBalanced 是如何實現的呢?要知道這個問題的答案,就得閱讀 LoadBalancerAutoConfiguration 的源碼。LoadBalancerAutoConfiguration 是實現客戶端負載均衡器的自動裝配類,隨著 Spring 的啟動而啟動,它的源碼內容有很多,我們這里截取部分核心的方法來看一下:
@Bean public?SmartInitializingSingleton?loadBalancedRestTemplateInitializerDeprecated(final?ObjectProvider<List<RestTemplateCustomizer>>?restTemplateCustomizers)?{return?()?->?{restTemplateCustomizers.ifAvailable((customizers)?->?{Iterator?var2?=?this.restTemplates.iterator();while(var2.hasNext())?{RestTemplate?restTemplate?=?(RestTemplate)var2.next();Iterator?var4?=?customizers.iterator();while(var4.hasNext())?{RestTemplateCustomizer?customizer?=?(RestTemplateCustomizer)var4.next();customizer.customize(restTemplate);}}});}; }這里的 this.restTemplates.iterator() 既所有被 @LoadBalanced 注解修飾的 RestTemplate 對象,所有被 @LoadBalanced 修飾的 RestTemplate 對象會被強轉為 RestTemplateCustomizer 對象,而這個對象的實現源碼如下:
@Bean @ConditionalOnMissingBean public?RestTemplateCustomizer?restTemplateCustomizer(final?LoadBalancerInterceptor?loadBalancerInterceptor)?{return?(restTemplate)?->?{List<ClientHttpRequestInterceptor>?list?=?new?ArrayList(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);}; }也就是所有被 @LoadBalanced 注解修飾的 RestTemplate 對象,會為其添加一個 loadBalancerInterceptor 的攔截器,攔截器的實現源碼如下:
public?class?LoadBalancerInterceptor?implements?ClientHttpRequestInterceptor?{private?LoadBalancerClient?loadBalancer;private?LoadBalancerRequestFactory?requestFactory;public?LoadBalancerInterceptor(LoadBalancerClient?loadBalancer,?LoadBalancerRequestFactory?requestFactory)?{this.loadBalancer?=?loadBalancer;this.requestFactory?=?requestFactory;}public?LoadBalancerInterceptor(LoadBalancerClient?loadBalancer)?{this(loadBalancer,?new?LoadBalancerRequestFactory(loadBalancer));}public?ClientHttpResponse?intercept(final?HttpRequest?request,?final?byte[]?body,?final?ClientHttpRequestExecution?execution)?throws?IOException?{URI?originalUri?=?request.getURI();String?serviceName?=?originalUri.getHost();Assert.state(serviceName?!=?null,?"Request?URI?does?not?contain?a?valid?hostname:?"?+?originalUri);return?(ClientHttpResponse)this.loadBalancer.execute(serviceName,?this.requestFactory.createRequest(request,?body,?execution));} }從上述源碼可以看出,@LoadBalanced 的執行流程是,被 @LoadBalanced 注解修飾的 RestTemplate 對象,會被 LoadBalancerInterceptor 攔截器所攔截,攔截之后使用 LoadBalancerClient 對象,按照負載均衡的策略獲取一個健康的服務實例,然后再通過服務實例的 IP 和端口,調用實例方法,從而完成服務請求。
總結
Nacos 調用 Restful 服務是通過內置的 Ribbon 框架實現的,它有兩種調用方法,通過代碼的方式或通過注解的方式完成調用。其中注解的方式使用起來比較簡單,只需要在 RestTemplate 對象上添加一個 @LoadBalanced 注解,就可以為請求對象賦予負載均衡的能力了。
是非審之于己,毀譽聽之于人,得失安之于數。
公眾號:Java中文社群
Java面試合集:https://gitee.com/mydb/interview
往期推薦Spring Cloud Alibaba Nacos 的 2 種健康檢查機制!
Nacos服務注冊與發現的2種實現方法!
Spring Cloud Alibaba Nacos路由策略之保護閾值!
總結
以上是生活随笔為你收集整理的SpringCloud Nacos + Ribbon 调用服务的 2 种方法!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大年初一,磊哥给大家发大红包啦!
- 下一篇: 操作系统大内核和微内核_操作系统中的内核