javascript
Spring Cloud微服务系列文,服务调用框架Feign
? ? 之前博文的案例中,我們是通過RestTemplate來調用服務,而Feign框架則在此基礎上做了一層封裝,比如,可以通過注解等方式來綁定參數,或者以聲明的方式來指定請求返回類型是JSON。? ? 這種“再次封裝”能給我們帶來的便利有兩點,第一,開發者無需像使用RestTemplate那樣過多地關注HTTP調用細節,第二,在大多數場景里,某種類型的調用請求會被在多個地方被多次使用,通過Feign能方便地實現這類“重用”。
?1?通過案例快速上手Feign
? ??在FeignDemo-Server項目里,搭建基于Eureka的服務器,該項目的端口號是8888,主機名是localhost,啟動后,能通過http://localhost:8888/eureka/查看注冊到Eureka服務器中的諸多服務提供者或調用者的信息。
? ? ?在FeignDemo-ServiceProvider項目的控制器類里,我們提供了一個sayHello方法,本項目提供服務的端口號是1111,對外提供的application name(服務名)是sayHelloServiceProvider,是向FeignDemo-Server服務器(也是Eureka服務器)的http://localhost:8888/eureka/注冊服務。而提供sayHello的方法如下所示,從中,我們能看到對應的RequestMapping值。
??? 1????? ??? @RequestMapping(value = "/hello/{username}", method = RequestMethod.GET?? )
??? 2????? ??? public String sayHello(@PathVariable("username") String username){
??? 3????? ????return "hello " + username;
??? 4????? }
? ? 上述Eureka服務器和客戶端的代碼,是復用架構師入門:搭建基本的Eureka架構(從項目里抽取)這篇文章里的代碼。
? ??這里我們將在FeignDemo-ServiceCaller項目里,演示通過Feign調用服務的方式。
? ??第一步,在pom.xml里,引入Eureka、Ribbon和Feign的相關包,關鍵代碼如下。其中,是通過第1行到第9行的代碼引入Eureka包,通過第10行到第13行的代碼引入Ribbon包,通過第14行到第17行的代碼引入Feign包。? ?
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-web</artifactId> 4 <version>1.5.4.RELEASE</version> 5 </dependency> 6 <dependency> 7 <groupId>org.springframework.cloud</groupId> 8 <artifactId>spring-cloud-starter-eureka</artifactId> 9 </dependency> 10 <dependency> 11 <groupId>org.springframework.cloud</groupId> 12 <artifactId>spring-cloud-starter-ribbon</artifactId> 13 </dependency> 14 <dependency> 15 <groupId>org.springframework.cloud</groupId> 16 <artifactId>spring-cloud-starter-feign</artifactId> 17 </dependency>? ??第二步,在application.yml里,通過第3行的代碼,定義本項目的名字叫callHelloByFeign,通過第5行的代碼,指定本項目是工作在8080端口。同時通過第9行的代碼,指定本項目是向http://localhost:8888/eureka/ (也就是FeignDemo-Server)這個Eureka服務器注冊。? ??
1 spring: 2 application: 3 name: callHelloByFeign 4 server: 5 port: 8080 6 eureka: 7 client: 8 serviceUrl: 9 defaultZone: http://localhost:8888/eureka/? ??第三步,在啟動類里,通過第1行的代碼,添加支持Feign的注釋,關鍵代碼如下。這樣,在啟動這個Eureka客戶端時,就可以引入Feign支持。? ??
1 @EnableFeignClients 2 @EnableDiscoveryClient 3 @SpringBootApplication 4 public class ServiceCallerApp 5 { 6 public static void main( String[] args ) 7 { SpringApplication.run(ServiceCallerApp.class, args); } 8 }? ??第四步,通過Feign封裝客戶端調用的細節,外部模塊是通過Feign來調用客戶端的,這部分的代碼是在Controller.java里。?? ??
1 省略必要的package和import的代碼 2 //通過注解指定待調用的服務名 3 @FeignClient("sayHelloServiceProvider") 4 //在這個接口里,通過Feign封裝客戶端的調用細節 5 interface FeignClientTool 6 { 7 @RequestMapping(method = RequestMethod.GET, value = "/hello/{name}") 8 String sayHelloInClient(@PathVariable("name") String name); 9 } 10 //Controller是控制器類 11 @RestController 12 public class Controller { 13 @Autowired 14 private FeignClientTool tool; 15 //在callHello方法是,是通過Feign來調用服務 16 @RequestMapping(value = "/callHello", method = RequestMethod.GET) 17 public String callHello(){ 18 return tool.sayHelloInClient("Peter"); 19 } 20 }? ??在Controller.java這個文件,其實定義了一個接口和一個類。在第5行的FeignClientTool接口里,我們封裝了Feign的調用業務,具體來說,是通過第3行的FeignClient注解,指定了該接口會調用“sayHelloServiceProvider“服務提供者的服務,而通過第8行的,則指定了調用該服務提供者中sayHelloInClient的方法。
? ? 而在第12行的Controller類里,先是在第14行里,通過Autowired注解,引入了FeignClientTool類型的tool類,隨后在第17行的callHello方法里,是通過tool類的sayHelloInClient方法,調用了服務提供者的相關方法。
? ? 也就是說,在callHello方法里,我們并沒有再通過RestTemplate,以輸入地址和服務名的方式調用服務,而是通過封裝在FeignClientTool(Feign接口)里的方法調用服務。
? ? 完成上述代碼后,我們可以通過如下的步驟查看運行效果。
? ? 第一步,啟動FeignDemo-Server項目,隨后輸入http://localhost:8888/,能看到注冊到Eureka服務器里的諸多服務。
? ? 第二步,啟動FeignDemo-ServiceProvider項目,隨后輸入http://localhost:1111/hello/Peter,能調用其中的服務,此時,能在瀏覽里看到“hello Peter”的輸出。
? ? 第三步,啟動FeignDemo-ServiceCaller項目,隨后輸入http://localhost:8080/callHello,同樣能在瀏覽里看到“hello Peter”的輸出。請注意,這里的調用是通過Feign完成的。? ??
2?通過比較其它調用方式,了解Feign的封裝性? ??
? ? 在之前的代碼里,我們是通過如下形式,通過RestTemplate對象來調用服務。
????????? 1????? RestTemplate template = getRestTemplate();
????????? 2????? ?????? String retVal = template.getForEntity("http://sayHello/hello/Eureka", String.class).getBody();
? ? 在第2行的調用中,我們需要指定url以及返回類型等信息。
? ? 之前我們還見過基于RestClient對象的調用方式,關鍵代碼如下。
????????? 1????? RestClient client = (RestClient)ClientFactory.getNamedClient("RibbonDemo");?
????????? 2????? ?HttpRequest request = HttpRequest.newBuilder().uri(new URI("/hello")).build();?
????????? 3????? HttpResponse response = client.executeWithLoadBalancer(request);?
? ? 其中是在第1行指定發起調用的RestClient類型的對象,在第2行里指定待調用的目標地址,隨后在第3行發起調用。
? ? 這兩種調用方式有著如下的共同點:調用時,需要詳細地知道各種調用參數,比如服務提供者的url,如果有需要通過Ribbon實現負載均衡等機制,也需要在調用時一并指定。
? ? 但事實上,這些調用方式的底層細節,應該向服務使用者屏蔽,比如在調用時,無需關注目標url等信息。這就好比某位老板要秘書去訂飛機票,作為服務使用者的老板只應當關心調用的結果,比如買到的飛機票是幾點開的,該去哪個航站樓登機,至于調用服務的底層細節,比如該到哪個訂票網站去買,服務使用者無需知道。
? ? 說得更專業些,這叫“解耦合”,即降低服務調動者和服務提供者之間的耦合度,這樣的好處是,一旦服務提供者改變了實現細節(沒改變服務調用接口),那么服務調用者部分的代碼無需改動。
? ? 我們再來回顧下通過Feign調用服務的方式。
????????? 1????? private FeignClientTool tool; //定義Feign類
????????? 2????? tool.sayHelloInClient("Peter"); //直接調用
? ? 第2行是調用服務,但其中,我們看不到任何服務提供者的細節,因為這些都在第1行引用的FeignClientTool類里封裝掉了。也就是說,通過基于Feign的調用方式,開發者能真正地做到“面向業務”,而無需過多地關注發起調用的細節。? ??
3 通過注解輸出調用日志
? ??在開發和調試階段,我們希望能看到日志,從而能定位和排查問題。這里,我們將講述在Feign里輸出日志的方法,以便讓大家能在通過Feign調用服務時,看到具體的服務信息。
? ? 這里我們將改寫FeignDemo-ServiceCaller項目。
? ? 改動點1:在application.yml文件里,增加如下的代碼,以開啟Feign客戶端的DEBUG日志模式,請注意,這里需要指定完成的路徑,就像第3行那樣。
????????? 1????? logging:
????????? 2????? ??level:
????????? 3????? ????com.controller.FeignClientTool: DEBUG??
? ? 改動點2:在這個項目的啟動類ServiceCallerApp.java里,增加定義日志級別的代碼,在第7行的feignLoggerLevel方法里,我們通過第8行的代碼,指定了Feign日志級別是FULL。? ??
1 //省略必要的pacakge和import代碼 2 @EnableFeignClients 3 @EnableDiscoveryClient 4 @SpringBootApplication 5 public class ServiceCallerApp{ 6 @Bean //定義日志級別是FULL 7 Level feignLoggerLevel() { 8 return Level.FULL; 9 } 10 //啟動類 11 public static void main( String[] args ) { 12 SpringApplication.run(ServiceCallerApp.class, args); 13 } 14 }? ? 完成后,依次運行Eureka服務器、服務提供者和服務調用者的啟動類,隨后在瀏覽器里輸入http://localhost:8080/callHello,即能在控制臺里看到DEBUG級別的日志,下面給出了部分輸出。
????????? 1????? 2018-06-17 12:18:27.296 DEBUG 208 --- [rviceProvider-2] com.controller.FeignClientTool?????????? : [FeignClientTool#sayHelloInClient] ---> GET http://sayHelloServiceProvider/hello/Peter?name=Peter HTTP/1.1
????????? 2????? 2018-06-17 12:18:27.296 DEBUG 208 --- [rviceProvider-2] com.controller.FeignClientTool?????????? : [FeignClientTool#sayHelloInClient] ---> END HTTP (0-byte body)
? ? 從第1行的輸出里,我們能看到以GET的方式向FeignClientTool類的sayHelloInClient方法發起調用,從第2行的輸出里,能看到調用結束。
? ? 在上文里,我們用的是FULL級別的日志,此外,還有NONE、BASIC和HEADERS這三種,在下表里,我們將詳細講述各級別日志的輸出情況。
| 日志輸出級別 | 描述 |
| NONE | 不輸出任何日志 |
| BASIC | 只輸出請求的方法,請求的URL和相應的狀態碼,以及執行的時間 |
| HEADERS | 除了會輸出BASIC級別的日志外,還會記錄請求和響應的頭信息 |
| FULL | 輸出所有的和請求和響應相關的日志信息 |
? ? ?一般情況下,在調試階段,可以把日志級別設置成FULL,等上線后,可以把級別調整為BASIC,因為在生產環境上,過多的日志反而會降低排查和定位問題的效率。? ??
4?壓縮請求和返回,以提升訪問效率
? ??在網絡傳輸過程中,如果我們能降低傳輸流量,那么即可提升處理請求的效率。尤其地,在一些日常訪問量比較高的網絡應用中,如果能降低處理請求(Request)和發送返回信息(Response)的時間,那么就能提升本站的吞吐量。
? ? 在Feign里,我們一般能通過如下的配置,來壓縮請求和響應。
? ? 第一,可以通過在application.yml里增加如下的配置,從而壓縮請求和返回信息。
????????? 1????? feign:
????????? 2????? ??compression:
????????? 3????? ????request:
????????? 4????? ??????enabled: true
????????? 5????? feign:
????????? 6????? ???compression:
????????? 7????? ?????response:
????????? 8????? ??????enabled: true
? ? 其中,前4行是壓縮請求,而后4行是壓縮返回。
? ? 第二,可以通過如下的代碼,設置哪類請求(或返回)將被壓縮,這里我們在第4行里,指定了兩類格式的請求將被壓縮。
????????? 1????? feign:????
????????? 2????? ??compression:
????????? 3????? ????request:
????????? 4????? ??????mime-types: text/xml,application/xml
? ? 第三,可以通過如下的代碼,指定待壓縮請求的最小值,這里是2048,也就是說,超過這個值的request才會被壓縮。
????????? 1????? feign:????
????????? 2????? ??compression:
????????? 3????? ????request:
? ? ? ? ? 4 ? ? ? ? ? ?min-request-size:2048
? ? 本文謝絕轉載。其它和Spring Cloud相關的博文如下:? ??
Spring Cloud微服務系列文,Hystrix與Eureka的整合?? 架構師系列文:通過Spring Cloud組件Hystrix合并請求?? 架構師入門:Spring Cloud系列,Hystrix與Eureka的整合?? Hystrix針對不可用服務的保護機制以及引入緩存?? 通過案例了解Hystrix的各種基本使用方式? Ribbon整合Eureka組件,以實現負載均衡? Spring Clould負載均衡重要組件:Ribbon中重要類的用法? 架構師入門:搭建雙注冊中心的高可用Eureka架構(基于項目實戰)? 架構師入門:搭建基本的Eureka架構(從項目里抽取)? 借助Maven入手Spring Boot第一個程序?轉載于:https://www.cnblogs.com/JavaArchitect/p/10448098.html
總結
以上是生活随笔為你收集整理的Spring Cloud微服务系列文,服务调用框架Feign的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jquery datatable的详细用
- 下一篇: 【Laravel-海贼王系列】第十三章,