SpringCloud之Feign聲明式調用原理及配置

1 什麼是Feign

  Feign是一種聲明式、模板化的HTTP客戶端(僅在Application Client中使用)。聲明式調用是指,就像調用本地方法同樣調用遠程方法,無需感知操做遠程http請求。
  Spring Cloud的聲明式調用, 能夠作到使用 HTTP請求遠程服務時能就像調用本地方法同樣的體驗,開發者徹底感知不到這是遠程方法,更感知不到這是個HTTP請求。Feign的應用,讓Spring Cloud微服務調用像Dubbo同樣,Application Client直接經過接口方法調用Application Service,而不須要經過常規的RestTemplate構造請求再解析返回數據。它解決了讓開發者調用遠程接口就跟調用本地方法同樣,無需關注與遠程的交互細節,更無需關注分佈式環境開發。html

2 使用Feign技術開發時的應用部署結構

              

  1. 在使用Feign技術開發Spring Cloud微服務時,須要先抽取要註冊發佈的服務標準,將這套標準經過接口的形式定義出來。
  2. 在Application Service端開發中,依賴抽取的服務標準接口工程,並對接口給予實現。
  3. 在Application Client端開發中,依賴抽取的服務標準接口工程,並應用接口信息和Feign技術,實現遠程服務的調用。  

  在總體微服務開發中,Eureka Server做爲註冊中心必不可少,註冊中心的做用不變,仍舊是註冊和發現服務。java

3 Feign入門案例

  3.1 建立服務標準工程

  服務標準工程,是用於定義Application Service須要對外發布的服務標準接口的工程。這個工程中定義的接口須要使用SpringMVC中的註解來描述,因此工程依賴的資源必須有SpringMVC。在案例中,爲了簡化依賴複雜度,使用spring-boot中的spring-boot-starter-web資源依賴實現SpringMVC技術的導入。git

<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路徑是什麼。github

/**
 * 微服務標準。
 * 是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和服務標準接口。web

<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來實現遠程服務調用。在這個過程當中,須要依賴於服務標準工程中定義的服務接口。spring

<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>

  啓動器:apache

/**
 * @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表明當前接口要調用的遠程服務的應用命名json

/**
 * 本地接口,繼承服務標準接口。
 * 在接口上增長註解@FeignClient,表明當前接口是一個Feign技術中的客戶端。
 * 須要發起遠程的http請求。
 * 註解有屬性name - 表明當前的FeignClient在請求application service的時候,是調用哪個服務?
 * 所謂的哪個服務,就是application service全局配置文件中的spring.application.name屬性值*/ @FeignClient(name="test-feign-application-service") public interface FirstClientFeignService extends FirstFeignService {
}

  控制器中能夠像調用本地定義服務對象那樣來調用遠程服務。api

/**
 * 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
相關文章
相關標籤/搜索