Feign(Web服務客戶端)學習筆記

一、Feign簡介

1.1、Feign是什麼?

Feign是一個聲明式(也就是通過註解編寫)的Web服務客戶端,使得編寫Web服務客戶端變得非常容易,值需要創建一個接口,然後在上面添加註解即可。
官網:https://github.com/OpenFeign/feign

1.2、Feign能幹什麼?

Feign旨在使編寫Java Http客戶端變得更容易。

在使用Spring Cloud Ribbon + RestTemplate時,利用RestTemplate對http請求的封裝處理,形成一套模板化的調用方法。但是在實際開發中,由於對服務依賴的調用可能不止一處,往往一個接口會被多次調用,所以通常都會針對每個微服務自行封裝一些客戶端類來保證這些依賴服務的調用。

Spring Cloud Feign在此基礎上做了進一步封裝,由他來幫助我們定義和實現依賴服務藉口的定義。在Spring Cloud Feign的實現下,我們只需創建一個接口,並使用註解的方式來配置它,即可完成對服務提供方的接口綁定,簡化了使用Spring Cloud Ribbon時自動封裝服務調用客戶端的開發量。

Spring Cloud Feign具備可插拔的註解支持,包括Feign註解和JAX-RS註解,同時還擴展了Spring MVC的註解支持。同時集成Ribbon和Eureka,以在使用Feign時提供Http客戶端的負載均衡;繼承了Hystrix以提供熔斷、降級的功能。

另外,對於Feign自身的一些主要組件,比如編碼器和解碼器等,都支持可插拔的方式,在有需要的時候可以方便地擴展和替換他們。

1.3、Feign的特性

1、可插拔的註解支持,包括Feign註解和JAX-RS註解
2、支持可插拔的HTTP編碼器和解碼器
3、支持Hystrix和他的Fallback
4、支持Ribbon的負載均衡
5、支持HTTP請求和響應壓縮

二、Feign的Hello World

2.1、Feign加入依賴jar

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-eureka</artifactId> 
</dependency>
<dependency> 
	<groupId>org.springframework.cloud</groupId> 
	<artifactId>spring-cloud-starter-feign</artifactId>
</dependency> 
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId> 
</dependency>

2.2、Feign啓動類添加註解

啓動類需要開啓Feign註解:

@SpringBootApplication 
@EnableDiscoveryClient 
@EnableFeignClients

2.3、定義接口

@FeignClient(value = "HelloInterface")
public interface UserService {
	@RequestMapping(value = "/userServiceProvider", method = RequestMethod.GET) 
	String sayHiFromClientOne(@RequestParam(value = "name") String name);
}

2.4、配置文件

配置文件類似於Ribbon的配置:
application.properties示例:

eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ 
server.port=8770
spring.application.name=service-feign

2.5、調用服務的Controller類

1、Feign集成了Ribbon,依然是利用了Ribbon維護了userService的服務列表信息,並且通過輪詢實現了客戶端的負載均衡。而與Ribbon不同的是,通過Feign值需要定義服務綁定接口,以聲明式的方法,優雅而簡單的實現了服務調用。

2、Spring Cloud Feign值支持Spring MVC 3.x系列,Spring MVC 4.x出現的一系列註解比如@GetMapping, @PostMapping, @PutMappting等Feign是不支持的。

示例:

@RestController
public class HelloController {
	@Autowired
	private HelloInterface hi;
	
	@RequestMapping("/hi")
	public String hi(@RequestParam(value="name") String name){
		System.out.println("now in client :" + name);
		String res = hi.userServiceProvider(name);
		return res;
	}
}

這樣只需要注入接口,調用遠程接口,就只需要調用本地的接口的方法。
這樣屏蔽了遠程調用的細節,遠程接口的代理實際上在遠程。

從而Feign簡化了遠程調用的流程,所以遠程調用接口的場景,可使用Feign來做。

而且Feign自帶支持Ribbon負載均衡。通過Eureka來做服務發現。

2.6、Feign中Ribbon的配置

由於Spring Cloud Feign的客戶端負載均衡是通過Spring Cloud Ribbon來實現的,所以可以直接通過配置Ribbon客戶端的方式來自定義各個服務客戶端調用的參數

2.7、調用流程

客戶端調用 --> Ribbon去選服務端 --> Hystrix調用 --> 遠程服務

三、Feign的參數綁定

1、@RequestParam : 綁定單個請求參數
2、@PathVariable :綁定URI模板變量值
3、@RequestHeader:綁定請求頭數據
4、@RequestBody:綁定請求的內容區數據,並能進行自動類型轉換等。

注意:
在定義各參數綁定的時候,@RequestParam和@RequestHeader等可以指定參數名稱的註解,他們的value值千萬不能少。在Spring MVC中,這些註解會根據指定參數名來做默認值,但在Feign中綁定參數必須通過value屬性來指明具體的參數名,不然會拋出IllegalStateException異常,value屬性不能爲空。

四、Feign的配置

4.1、Feign的默認配置

Spring Cloud的Feign的一箇中心概念就是客戶端。每個Feign客戶端都是組合的組件的一部分,他們一起工作以按需調用遠程服務器,並且該集合具有將其作爲使用@FeignClient註釋的參數名稱。

Spring Cloud使用FeignClientsConfiguration創建一個新的集合,作爲每個命名客戶端的ApplicationContext(應用上下文),這包含feign.Decoder, feign.Encoder和feign.Contract。

Spring Cloud Netflix默認爲Feign提供以下bean:
1、Decoder feignDecoder:ResponseEntityDecoder(其中包含Spring Decoder)
2、Encoder feignEncoder:SpringEncoder
3、Logger feignLogger:Slf4jLogger
4、Contract feignContract:SpringMvcContract
5、Feign.Builder feignBuilder:HystrixFeign.Builder
6、Client feignClient:如果Ribbon啓用,則爲LoadBalancerFeignClient,否則將使用默認的feign客戶端

可以自定義FeignClientsConfiguration以頑強控制這一系列的配置。

4.2、Feign的自定義配置

寫一個自定義配置類,注意不要放到當前ComponentScan的範圍下:

@Configuration
public class MyConf {
	@Bean
	public Contract feignContract() {
		return new feign.Contract.Default(); 
	}
}

定義的時new feign.Contract.Default(), 所有在UserService接口中只能使用Feign自己的註解url方式,使用Spring MVC的註解就會報錯。

寫好配置後,通過設置@FeignClient的configuration來使用,如:
@FeignClient(value=“userService”, configuration=MyConf.class)

可以爲每個Feign客戶端都配置自己的默認配置。

五、@FeignClient註解

@FeignClient標籤的常用屬性如下:
1、name(value):指定FeignClient的名稱,如果項目使用了Ribbon,name屬性會作爲微服務的名稱,用於服務發現。
2、url:url一般用於調試,可以手動指定@FeignClient調用的地址。
3、configuration:Feign的配置類,可以自定義Feign的Encoder, Decoder, LogLevel, Contract。
4、fallback:定義容錯的處理類,當調用遠程接口失敗或超時時,會調用對應接口的容錯邏輯,fallback指定的類必須實現@FeignClient標記的接口,並使用@Component註解。
5、fallbackFactory:工廠類,用於生產fallback類實例,通過這個屬性,我們可以實現每個接口通用的容錯邏輯,減少重複的代碼。
6、path:定義當前FeignClient的統一前綴,添加到Feign訪問服務的訪問路徑上。
7、decode404:當發生http 404錯誤時,如果該字段爲true, 會調用decoder運行解碼,否則拋出FeignException。

注意:serviceId屬性已被棄用,有利於name屬性
以前,使用url屬性,不需要name屬性,現在需要使用name屬性。

六、Feign的@Primary

當使用Feign與Hystrix回退時,在同一類型的ApplicaitonContext中有多個bean。這將導致@Autowired不起作用,因爲沒有一個bean標記爲主。

爲了解決這個問題,Spring Cloud Netflix將所有Feign實例標記爲@Primary,所以Spring framework將指導要注入哪個bean。在某些情況下,這可能是不可取的。要關閉此行爲,將@FeignClient的primary屬性設置爲false, 如:
@FeighCLient(name=“hello”, primary=false)

七、自定義Feign的HttpClient

Feign在默認情況下使用過的是JDK原生的URLConnection發送Http請求,沒有連接池,但是對每個地址會保持一個長連接,即利用http的persistence connection。

可以用Apache的Http client替換Feign原始的http client,從而獲取連接池、超時時間等於性能息息相關的控制能力。Spring Cloud從Brixtion.SR5版本開始支持這種替換,首先在項目中聲明Apache HTTP Client和feign-httpclient依賴:

<dependency> 
	<groupId>org.apache.httpcomponents</groupId> 
	<artifactId>httpclient</artifactId>
</dependency>
<dependency>
	<groupId>com.netflix.feign</groupId> 
	<artifactId>feign-httpclient</artifactId> 
	<version>8.18.0</version>
</dependency>

然後在application.properties中添加:

feign.httpclient.enabled=true

八、自動以編碼器和解碼器

8.1、自定義Encode, Decode, ErrorDecode

Feign將方法簽名中的方法參數對象序列化爲請求參數,放到HTTP請求的過程中,是由編碼器(Encoder)完成的。同理,將HTTP想用數據反序列化爲java對象,是由解碼器(Decoder)完成的。

默認情況下,Feign會將表用@RequestParam註解的參數轉換爲字符串添加到URL中,將沒有註解的參數通過Jackson轉換成json放到請求體中。

在Spring Cloud環境下,Feign的Encoder只會用來編碼沒有添加註解的參數。如果你自定義了Encoder,那麼只有在編碼沒有註解參數的時候,纔會調用你的Encoder。

對於Decoder,默認會委託給SpringMVC中的MappingJackson2HttpMessageConverter類進行解碼。只有當狀態碼不再200~300之間的ErrorDecoder纔會被調用。ErrorDecoder的作用是可以根據http響應信息返回一個異常,該異常可以在調用Feign接口的地方被捕獲。我們目前就通過ErrorDecoder來使Feign接口拋出業務異常一共調用者處理。

九、FeignClient的工作過程

FeignClient相當於Spring Cloud的RPC,實現過程如下:
1、首先通過@EnableFeignClients註解開啓FeignClient
2、根據Feign的規則實現接口,並加@FeignClient註解
3、程序啓動後,會進行包掃描,掃描所有有@FeignClient註解的類,並將這些信息注入到IOC容器中。
4、當接口的方法被調用,通過JDK的代理,來生產具體的RestTemplate。
5、RequestTemplate再生產調用的Request
6、Request交給Client去處理,其中Client可以是HttpUrlConnection,HttpClient,也可以是Okhttp。
7、最後Client被封裝到LoadBalanceClient類,這個類結合類Ribbon做到了負載均衡,真正發送請求出去。

十、使用繼承特性減少代碼冗餘

當使用Spring MVC的註解來綁定服務接口的時候,幾乎可以完全從服務提供方的Controller中複製操作,構建出響應的服務客戶端綁定接口。計入存在這麼多複製操作,我們自然需要考慮這部分內容是否可以得到進一步的抽象。Spring Cloud Feign中,針對該問題提供了繼承特性來幫助解決這些複製操作,以進一步減少編碼量。

基本方法,就是把接口提出來,把model提出來,然後provider和client都使用這個工程:
Provider這邊在Controller中不再包含以往會定義的映射註解@RequestMapping,而參數的註解定義在重寫的時候自動帶過來了,這個類中,出來要實現接口邏輯之外,只需要增加了@RequestController註解使該類成爲一個REST接口類。
Consumer這邊直接注入Feign標註的接口,完全像調用本地接口一樣。

注意:
1、Figh接口只能支持一層繼承,而且不能多繼承
2、官方不建議在服務器和客戶端直接共享接口,因爲他引入了緊耦合,並且實際上並不適用於當前形勢的Spring MVC(方法參數映射不被繼承)

十一、Hystrix配置

11.1、概述

Spring Cloud Feign中集成了Hystrix, Spring Cloud Feign客戶端的方法都封裝到Hystrix命令中進行服務保護。

11.2、全局配置

直接適用它的默認配置前綴hystrix.command.default就可以進行配置,比如設置全局的超時時間,Hystrix默認的超時時間是1秒:
hystrix.command.default.exection.isolationtrhead.timeoutInMilliseconds=5000
在對hystrix進行設置之前,需要確認Feign的Hystrix的功能是開啓的。

11.3、禁用Hystrix

可以通過設置feign.hystrix.enabled爲false,或者使用hystrix.command.default.exection.timeout.enabled=false來關閉熔斷功能。

如果不想全局關閉Hystrix支持,而只想針對某個服務客戶端關閉Hystrix支持時,需要通過使用@Scope(“prototype”)註解爲指定的客戶端配置Feigh.Builder實例,如:

@Configuration
public class DisableHystrixConfiguration {
	@Bean
	@Scope("prototype")
	public Feign.Builder feignBuilder() {
		return feign.builder();
	}
}

在該服務的Feign接口中引入該配置:
@FeignClient("value=“user.service”,configuration=DisableHystrixConfiguration.class)

11.4、指定命令配置

在實際應用中可能會根據實際業務情況指定出不同的配置方案,可採用hystrix.command.<commandKey>作爲前綴,而<commendKey>默認情況下,會採用feign客戶端中的方法名作爲表示。

需要注意的時,由於方法名有可能會重複,這個時候相同的方法名的hystrix配置會共用,所以在進行方法與配置的時候需要做好統一的規劃。

11.5、服務降級配置

Spring Cloud Feign在定義服務客戶端的時候,與Spring Cloud Ribbon有很大的差別,由於HystrixCommand定義被封裝,無法通過@HystrixCommand註解的fallback參數那也來指定具體的服務降級處理方法。Spring Cloud Feign提供了另外一種簡單的方式:
定義一個Feign客戶端的服務降級類UserServiceFallback,實現UserService接口,其中每個重寫方法的實現邏輯都可以用來定義響應的服務降級邏輯;在服務綁定接口中,通過@FeignClient註解的fallback屬性來定製對應的服務降級實現類。

十二、Feign的請求壓縮

Spring Cloud Feign支持對請求與響應進行GZIP壓縮,以減少通信過程中的性能損耗,值需要通過下面的兩個參數設置,就能開啓請求與響應的壓縮功能:

feign.compression.reqeust.enabled=true
feign.compression.response.enabled=true

同時,還可以對請求壓縮做一些更細緻的設置,比如下面的配置內容指定壓縮的請求數量類型,並設置了請求壓縮的大小下線,只有超過這個大小的請求才會進行壓縮:

feign.compression.request.enabled=true
feign.compression.request.mime-type=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048

十三、Feign的日誌配置

Spring Cloud Feign在構建被@FeignClient註解修飾的服務客戶端時,回味每一個客戶端都創建一個feign.Logger實例,可以配置logging.level.<feignClient>的參數配置格式來開啓指定feign客戶端的debug日誌,其中<feignClient>爲feign客戶端定義接口的完整路徑,如:

logging.level.com.test111.springcloud_feign_api.UserService2=debug

注意:只添加該配置還無法實現對debug日誌的輸出。這是因爲feign客戶端默認的Logger.Level對象定義爲NONE級別,該級別不會記錄任何Feign調用過程中的信息,所以需要調整它的級別,針對全局的日誌級別,可以在應用主類中加入:

@Bean
Logger.Level feignLoggerLevel() {
	return Logger.Level.FULL;
}

也可以實現配置類,然後在Feign客戶端來指定配置類以實現不同的日誌級別。
對於Feign的Logger級別注意有下面4類,可根據十幾需要進行調整使用:
1、None:不記錄任何信息
2、basic:僅記錄請求方法,url以及相應狀態碼和執行時間。
3、headers:除了記錄basic級別的信息之外,還會記錄請求和響應頭信息
4、FULL:記錄所有請求與響應的明細,包括頭信息、請求體、元數據等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章