1 什么是Feign
Feign是一種聲明式、模板化的HTTP客戶端(僅在Application Client中使用)。聲明式調用是指,就像調用本地方法一樣調用遠程方法,無需感知操作遠程http請求。
Spring Cloud的聲明式調用, 可以做到使用 HTTP請求遠程服務時能就像調用本地方法一樣的體驗,開發者完全感知不到這是遠程方法,更感知不到這是個HTTP請求。Feign的應用,讓Spring Cloud微服務調用像Dubbo一樣,Application Client直接通過接口方法調用Application Service,而不需要通過常規的RestTemplate構造請求再解析返回數據。它解決了讓開發者調用遠程接口就跟調用本地方法一樣,無需關注與遠程的交互細節,更無需關注分布式環境開發。
2 使用Feign技術開發時的應用部署結構
- 在使用Feign技術開發Spring Cloud微服務時,需要先抽取要注冊發布的服務標准,將這套標准通過接口的形式定義出來。
- 在Application Service端開發中,依賴抽取的服務標准接口工程,並對接口給予實現。
- 在Application Client端開發中,依賴抽取的服務標准接口工程,並應用接口信息和Feign技術,實現遠程服務的調用。
在整體微服務開發中,Eureka Server作為注冊中心必不可少,注冊中心的作用不變,仍舊是注冊和發現服務。
3 Feign入門案例
3.1 創建服務標准工程
服務標准工程,是用於定義Application Service需要對外發布的服務標准接口的工程。這個工程中定義的接口需要使用SpringMVC中的注解來描述,所以工程依賴的資源必須有SpringMVC。在案例中,為了簡化依賴復雜度,使用spring-boot中的spring-boot-starter-web資源依賴實現SpringMVC技術的導入。
<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 http://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>1.5.16.RELEASE</version> </parent> <groupId>com.bjsxt</groupId> <artifactId>spring-cloud-feign-serviceapi</artifactId> <version>1.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!-- 主要使用其中的SpringMVC相關技術。將請求的URL和服務的方法耦合到一起。 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
定義的服務接口和普通的服務接口沒有太大的區別,但是為了讓Feign技術可以識別到服務的訪問URL,需要使用SpringMVC注解@RequestMapping來描述接口中定義的方法,代表方法對外提供服務時監聽的URL路徑是什么。
/** * 微服務標准。 * 是Application Service要提供的服務的標准。 * 也是Application Client要調用遠程服務的標准。 * 就是一個普通的接口。 */ public interface FirstFeignService { /** * 測試GET請求的方法。 * 請求不傳遞任何的參數。 * 請求地址是 - /testFeign -> http://ip:port/testFeign * @return */ @RequestMapping(value="/testFeign", method=RequestMethod.GET) public List<String> testFeign(); /** * 測試GET請求傳遞一個普通的參數。 /get?id=xxx * 在為Feign定義服務標准接口的時候,處理請求參數的方法參數,必須使用@RequestParam注解描述。 * 並且,無論方法參數名和請求參數名是否一致,都需要定義@RequestParam注解的value/name屬性。 * @return */ @RequestMapping(value="/get", method=RequestMethod.GET) public FeignTestPOJO getById(@RequestParam(value="id") Long id); /** * 測試使用POST請求傳遞一個普通參數 * 在Feign技術中,默認的發起POST請求的時候,請求的參數,都是在請求體中使用JSON數據傳遞的。 * 不是name=value對傳遞的。 * 必須使用@RequestBody注解來解析請求體中的數據。 * * 如果使用POST方式發起請求,傳遞多個普通參數,是使用請求頭傳遞的參數。可以使用@RequestParam注解來處理請求參數。 * POST請求的請求體類型還是application/json。feign會通過請求頭傳遞多個請求參數: /xxx?a=xxx&b=xxx&c=xxx * @return */ @RequestMapping(value="/get", method=RequestMethod.POST) public FeignTestPOJO getByIdWithPOST(@RequestBody Long id); /** * 使用GET請求傳遞多個普通參數。 /add?id=xxx&name=xxx * 必須使用@RequestParam注解處理請求參數。 * @return */ @RequestMapping(value="/add", method=RequestMethod.GET) public FeignTestPOJO add(@RequestParam("id") Long id, @RequestParam("name") String name); /** * 錯誤案例 * 使用GET請求傳遞特殊參數。自定義類型的參數。 * 在Feign發起的默認的請求中,GET請求方式不能傳遞自定義類型數據。只能通過POST請求傳遞。 * @return */ @RequestMapping(value="/addWithGET", method=RequestMethod.GET) public FeignTestPOJO add(@RequestBody FeignTestPOJO pojo); /** * 使用POST請求傳遞特殊參數。自定義類型的參數。 * 默認環境中,只要是Feign發起的POST請求,請求參數都是JSON數據。 * 必須使用@RequestBody處理。 * @return */ @RequestMapping(value="/addWithPOST", method=RequestMethod.POST) public FeignTestPOJO addWithPOST(@RequestBody FeignTestPOJO pojo); }
3.2 創建Application Service工程
服務提供者在開發的時候,需要實現服務標准工程中定義的服務接口,並注冊服務到Eureka Server注冊中心上,所以需要依賴的資源必須有eureka和服務標准接口。
<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 http://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>1.5.16.RELEASE</version> </parent> <groupId>com.bjsxt</groupId> <artifactId>spring-cloud-feign-appservice</artifactId> <version>1.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Edgware.SR4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- web啟動器。 springmvc相關內容 --> <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> <!-- spring cloud 默認配置啟動器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!-- spring cloud Eureka Client 啟動器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- 自定義的服務標准工程。 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>spring-cloud-feign-serviceapi</artifactId> <version>1.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
在Spring Cloud微服務架構中,服務是由Controller對外提供的,是基於HTTP協議發布的REST服務。所以實現服務接口的是控制器。注意,服務不代表服務層代碼。
/** * 自定義的服務控制器 * 對外提供服務的Application Service。 * 不能隨便的定義服務了。如果想讓Application Client可以通過Feign技術訪問當前類型提供的服務, * 則必須遵循服務標准。 */ @RestController public class TestFeignAppServiceController implements FirstFeignService { /** * 因為當前的方法都是實現接口FirstFeignService中的方法。 * 而在接口中,已經將請求URL和方法耦合到一起了。 * 所以在當前的控制器中,不能重復定義@RequestMapping來約束請求URL。 */ @Override public List<String> testFeign() { List<String> result = new ArrayList<>(); result.add("test feign"); result.add("this is first spring cloud with feign"); return result; } @Override public FeignTestPOJO getById(Long id) { return new FeignTestPOJO(id, "getById"); } /** * 如果方法參數是處理POST請求的JSON數據的。 * 那么還是需要定義@RequestBody注解來描述方法參數的。 */ @Override public FeignTestPOJO getByIdWithPOST(@RequestBody Long id) { return new FeignTestPOJO(id, "getByIdWithPOST"); } @Override public FeignTestPOJO add(Long id, String name) { System.out.println( "add(Long id, String name)" ); return new FeignTestPOJO(id, name); } /** * 在默認的情況下,Feign不能通過GET請求傳遞自定義類型的請求參數。 */ @Override public FeignTestPOJO add(@RequestBody FeignTestPOJO pojo) { System.out.println( "add(@RequestBody FeignTestPOJO pojo)" ); return pojo; } @Override public FeignTestPOJO addWithPOST(@RequestBody FeignTestPOJO pojo) { System.out.println( "addWithPOST(@RequestBody FeignTestPOJO pojo)" ); return pojo; } }
3.3 創建Application Client工程
服務消費者在開發的時候,需要在Eureka Server中發現可用服務,並通過Feign來實現遠程服務調用。在這個過程中,需要依賴於服務標准工程中定義的服務接口。
<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 http://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>1.5.16.RELEASE</version> </parent> <groupId>com.bjsxt</groupId> <artifactId>spring-cloud-feign-appclient</artifactId> <version>1.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Edgware.SR4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <!-- spring cloud 默認配置啟動器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!-- spring cloud Eureka Client 啟動器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- feign啟動器。封裝了所有的feign相關資源的jar包。提供的是默認環境。 feign技術在請求遠程服務的時候,依托於http協議。 默認底層使用JDK提供的HttpUrlConnection來實現http遠程訪問的。 httpurlconnection技術不支持http連接池。所以效率上較低。 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <!-- 服務標准工程。 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>spring-cloud-feign-serviceapi</artifactId> <version>1.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
啟動器:
/** * @EnableFeignClients - 啟動FeignClient技術。 * 開啟Feign的應用。 * * @EnableDiscoveryClient - 啟動發現機制。 * 就是輔助Feign技術,發現服務,定義服務動態代理的輔助技術。 * * @EnableEurekaClient 注解刪除。是使用Discovery來發現服務的。discovery是輔助feign技術的一個發現客戶端。 */ @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class FeignApplicationClientApplication { public static void main(String[] args) { SpringApplication.run(FeignApplicationClientApplication.class, args); } }
在通過Feign來實現遠程服務調用時,需要提供一個本地接口來繼承服務標准工程提供的服務接口。這個本地接口不需要給予任何實現,在底層Spring容器會為這個接口提供一個基於JDK實現的代理對象,這個代理對象由Feign技術提供具體的HandlerInterceptor邏輯,實現遠程的調用。實現過程類似通過代碼調用LoadBalancerClient實現的Rest遠程訪問。
而本地接口繼承服務標准接口后,需要提供注解@FeignClient,注解的屬性name代表當前接口要調用的遠程服務的應用命名。
/** * 本地接口,繼承服務標准接口。 * 在接口上增加注解@FeignClient,代表當前接口是一個Feign技術中的客戶端。 * 需要發起遠程的http請求。 * 注解有屬性name - 代表當前的FeignClient在請求application service的時候,是調用哪一個服務? * 所謂的哪一個服務,就是application service全局配置文件中的spring.application.name屬性值。 */ @FeignClient(name="test-feign-application-service") public interface FirstClientFeignService extends FirstFeignService { }
控制器中可以像調用本地定義服務對象那樣來調用遠程服務。
/** * Application Client中的控制器。是和用戶直接交互的控制器。 * 像平時開發代碼一樣。調用本地的一個service接口。通過service接口遠程訪問Application Service。 */ @RestController public class TestFeignAppClientController { /** * 本地定義的服務接口。用於實現遠程調用application service的接口。 */ @Autowired private FirstClientFeignService service; /** * 無參 * @return */ @GetMapping("/testFeign") public List<String> testFeign(){ System.out.println(this.service.getClass().getName()); return this.service.testFeign(); } }
4 參數處理
在Feign處理遠程服務調用時,傳遞參數是通過HTTP協議傳遞的,參數存在的位置是請求頭或請求體中。請求頭傳遞的參數必須依賴@RequestParam注解來處理請求參數,請求體傳遞的參數必須依賴@RequestBody注解來處理請求參數。
上述的要求是指Feign技術傳遞參數,也就是Application Client遠程調用Application Service過程。
4.1 處理請求頭傳參
使用請求頭傳遞參數時,定義的服務標准接口中,必須使用@RequestParam注解來描述方法參數,且注解屬性value/name必須指定,代表從請求頭中獲取的請求參數命名是什么。
在默認環境中,使用請求頭傳遞參數時,Application Service中的控制器無法使用SpringMVC中的自動對象封裝能力。只能處理簡單數據類型。如:數學類型,字符串類型,數組類型等。無法處理自定義對象類型。
4.2 處理請求體傳參
使用請求體傳遞參數時,定義的服務標准接口中,必須使用@RequestBody注解來描述方法參數,因為Feign技術發起的POST請求,請求參數是使用JSON字符串來傳遞的,且form表單類型為RAW。
使用請求體傳遞參數時,Application Service中的控制器必須使用@RequestBody注解來描述方法參數,且RAW類型的form表單上傳的是一個文本內容,只能轉換為唯一的一個參數。
如果使用POST方式提交請求,並傳遞多個普通類型的參數時,Feign不會通過請求體傳遞參數,是通過請求頭傳遞的參數。也就是請求路徑為 : http://applicationserver:port/url?paramName=paramValue。
5 Feign通訊優化:GZIP壓縮
5.1 GZIP簡介
gzip介紹:gzip是一種數據格式,采用deflate算法壓縮數據;gzip是一種流行的數據壓縮算法,應用十分廣泛,尤其是在Linux平台。
gzip能力:當Gzip壓縮到一個純文本數據時,效果是非常明顯的,大約可以減少70%以上的數據大小。
gzip作用:網絡數據經過壓縮后實際上降低了網絡傳輸的字節數,最明顯的好處就是可以加快網頁加載的速度。網頁加載速度加快的好處不言而喻,除了節省流量,改善用戶的瀏覽體驗外,另一個潛在的好處是Gzip與搜索引擎的抓取工具有着更好的關系。例如 Google就可以通過直接讀取gzip文件來比普通手工抓取更快地檢索網頁。
5.2 HTTP協議中關於壓縮傳輸的規定
如圖所示:
第一:客戶端向服務器請求頭中帶有:Accept-Encoding:gzip, deflate 字段,向服務器表示,客戶端支持的壓縮格式(gzip或者deflate),如果不發送該消息頭,服務器是不會壓縮的。
第二:服務端在收到請求之后,如果發現請求頭中含有Accept-Encoding字段,並且支持該類型的壓縮,就對響應報文壓縮之后返回給客戶端,並且攜帶Content-Encoding:gzip消息頭,表示響應報文是根據該格式壓縮過的。
第三:客戶端接收到響應之后,先判斷是否有Content-Encoding消息頭,如果有,按該格式解壓報文。否則按正常報文處理。
5.3 在Feign技術中應用GZIP壓縮
在Spring Cloud微服務體系中,一次請求的完整流程如下:
在整體流程中,如果使用GZIP壓縮來傳輸數據,涉及到兩次請求-應答。而這兩次請求-應答的連接點是Application Client,那么我們需要在Application Client中配置開啟GZIP壓縮,來實現壓縮數據傳輸。
5.3.1 只配置Feign請求-應答的GZIP壓縮
這里只開啟Feign請求-應答過程中的GZIP,也就是瀏覽器-Application Client之間的請求應答不開啟GZIP壓縮。在全局配置文件中,使用下述配置來實現Feign請求-應答的GZIP壓縮:
# feign gzip # 局部配置。只配置feign技術相關的http請求-應答中的gzip壓縮。 # 配置的是application client和application service之間通訊是否使用gzip做數據壓縮。 # 和瀏覽器到application client之間的通訊無關。 # 開啟feign請求時的壓縮, application client -> application service
feign.compression.request.enabled=true
# 開啟feign技術響應時的壓縮, application service -> application client
feign.compression.response.enabled=true
# 設置可以壓縮的請求/響應的類型。
feign.compression.request.mime-types=text/xml,application/xml,application/json
# 當請求的數據容量達到多少的時候,使用壓縮。默認是2048字節。
feign.compression.request.min-request-size=512
5.3.2 配置全局的GZIP壓縮
在全局配置文件中配置下述內容,來開啟所有請求-應答中的GZIP壓縮,這里使用的是Spring Boot中的GZIP技術。在Spring Boot中已經集成了GZIP壓縮技術,並對所有的請求-應答實現GZIP數據壓縮。Spring Cloud中已經集成了Spring Boot技術,所以在配置文件中可以開啟Spring Boot中的GZIP壓縮技術,對完整流程中與當前節點(Application Client)所有相關的請求-應答開啟GZIP壓縮。
# spring boot gzip # 開啟spring boot中的gzip壓縮。就是針對和當前應用所有相關的http請求-應答的gzip壓縮。
server.compression.enabled=true
# 哪些客戶端發出的請求不壓縮,默認是不限制
server.compression.excluded-user-agents=gozilla,traviata
# 配置想壓縮的請求/應答數據類型,默認是 text/html,text/xml,text/plain
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain
# 執行壓縮的閾值,默認為2048
server.compression.min-response-size=512
6 Feign的通訊優化:HttpClient客戶端替換及HTTP連接池
兩台服務器建立HTTP連接的過程是很復雜的一個過程,涉及到多個數據包的交換,並且也很耗時間。HTTP連接需要的3次握手4次揮手開銷很大,這一開銷對於大量的比較小的HTTP消息來說更大。
那么如何來優化HTTP連接性能?我們可以采用HTTP連接池來提升性能,連接池可以節約大量的3次握手4次揮手的時間,這樣能大大提升吞吐率。
6.1 Feign技術的底層實現
Feign的HTTP客戶端支持3種框架,分別是;HttpURLConnection、HttpClient、OKHttp。Feign中默認使用HttpURLConnection。
HttpURLConnection是JDK自帶的HTTP客戶端技術,並不支持連接池,如果要實現連接池的機制,還需要自己來管理連接對象。對於網絡請求這種底層相對復雜的操作,如果有可用的其他方案,也沒有必要自己去管理連接對象。
Apache提供的HttpClient框架相比傳統JDK自帶的HttpURLConnection,它封裝了訪問http的請求頭,參數,內容體,響應等等;它不僅使客戶端發送HTTP請求變得容易,而且也方便了開發人員測試接口(基於Http協議的),即提高了開發的效率,也方便提高代碼的健壯性;另外高並發大量的請求網絡的時候,還是用“HTTP連接池”提升吞吐量。
OKHttp是一個處理網絡請求的開源項目,是安卓端最火熱的輕量級框架,由移動支付Square公司貢獻用於替代HttpUrlConnection和Apache HttpClient。OKHttp擁有共享Socket,減少對服務器的請求次數,通過連接池,減少了請求延遲等技術特點。
本案例中,通過替換Feign底層的HTTP客戶端實現為HttpClient,來提升Feign的通訊性能。
6.2 Feign中應用HttpClient
資源依賴:Feign技術發起請求的位置是Application Client端,下述依賴只需要在Application Client所在工程中添加即可。
<!-- httpclient相關依賴 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
修改全局配置文件:
# 開啟feign技術對底層httpclient的依賴。 切換底層實現技術。
feign.httpclient.enabled=true
提供連接池配置類:
/** * 配置類型 * 用於提供一個HTTP連接池,並實現連接池管理。 * 主要目的是,提供一個合理的資源回收方式。 */ @Configuration public class HttpClientConfiguration { @Bean public HttpClient httpClient(){ System.out.println("init feign httpclient configuration " ); // 生成默認請求配置 RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); // 超時時間 requestConfigBuilder.setSocketTimeout(5 * 1000); // 連接時間 requestConfigBuilder.setConnectTimeout(5 * 1000); RequestConfig defaultRequestConfig = requestConfigBuilder.build(); // 連接池配置 // 長連接保持30秒 final PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.MILLISECONDS); // 總連接數 pollingConnectionManager.setMaxTotal(5000); // 同路由的並發數 pollingConnectionManager.setDefaultMaxPerRoute(100); // httpclient 配置 HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); // 保持長連接配置,需要在頭添加Keep-Alive httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()); httpClientBuilder.setConnectionManager(pollingConnectionManager); httpClientBuilder.setDefaultRequestConfig(defaultRequestConfig); HttpClient client = httpClientBuilder.build(); // 啟動定時器,定時回收過期的連接, 最重要。 如果沒有定義回收策略。連接池會在運行一段時間后失效。 Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { pollingConnectionManager.closeExpiredConnections(); pollingConnectionManager.closeIdleConnections(5, TimeUnit.SECONDS); } }, 10 * 1000, 5 * 1000); System.out.println("===== Apache httpclient 初始化連接池==="); return client; } }
使用HttpClient客戶端替換HttpURLConnection,僅需修改Application Client,其余無需修改。當使用HttpClient技術作為Feign的底層HTTP技術應用時,使用GET請求方式請求頭傳遞自定義類型對象是可行的,只要在服務標准對象中定義的方法參數上增加注解@RequestBody即可處理。
7 配置Feign中的請求超時
請求超時/請求時間: 客戶端發起請求,服務端響應並成功建立雙向鏈接的時間。
鏈接超時/鏈接時間: 雙向鏈接建立成功后,需要多久必須得到服務端的響應結果。
在Feign聲明式遠程調用中,負載均衡還是使用的Ribbon技術。而Ribbon技術默認的鏈接超時是1秒,也就是1秒內Application Service沒有處理Application Client的請求,且鏈接請求處理后,1秒之內沒有返回響應,Application Client都會拋出超時異常。在商業項目中,部分服務是很難在1秒之內處理鏈接,並在處理鏈接后1秒之內返回響應的,所以配置超時信息就很有必要了。
定義超時配置的時候,建議為每個服務定義超時策略。如果有部分服務可以使用同樣的超時策略,可以使用全局配置。指定服務配置超時策略,會覆蓋全局配置。
超時策略的使用優先級: 指定服務的超時策略 -> 全局配置的超時策略 -> 默認的超時策略。
對Ribbon鏈接超時的配置都在全局配置文件中定義。具體如下。
7.1 全局服務配置
全局服務配置粒度粗糙。商業項目中不同的服務對響應時長的限制也不同,全局服務配置不推薦應用。
# 全局服務配置 # 請求連接的超時時間 默認的時間為1秒
ribbon.ConnectTimeout=1000
# 請求處理的超時時間
ribbon.ReadTimeout=5000
7.2 部分服務配置
部分服務配置粒度細致,可以為每個具體服務配置不同的超時信息,推薦使用。
# 部分服務配置 服務名.ribbon.屬性=屬性值 # 對所有操作請求都進行重試
spring-cloud-feign-appservice.ribbon.OkToRetryOnAllOperations=true
# 對當前實例的重試次數
spring-cloud-feign-appservice.ribbon.MaxAutoRetries=2
# 請求連接的超時時間
spring-cloud-feign-appservice.ribbon.ConnectTimeout=3000
# 請求處理的超時時間
spring-cloud-feign-appservice.ribbon.ReadTimeout=3000