學習SpringCloud Feign帶你從0到1

1、什麼是Feign

​ Feign是一種聲明式、模板化的HTTP客戶端(僅在consumer中使用)php

2、什麼是聲明式,有什麼做用,解決什麼問題?

​ 聲明式調用就像調用本地方法同樣調用遠程方法,無感知遠程HTTP請求。html

​ 1.SpringCloud的聲明式調用,能夠作到使用HTTP請求遠程服務時就像調用本地方法同樣的體驗,開發者徹底感知不到這是遠程方法。更感知不到這是一個HTTP請求java

​ 2.它像Dubbo同樣,consumr直接調用接口方法調用provider,而不須要經過常規的Http Client構造請求再解析返回數據。web

​ 3.它解決了讓開發者調用遠程接口就跟調用本地方法同樣,無需關注與遠程的交互細節,更無需關注分佈式環境開發。算法

3、編寫Feign的入門案例

1.需求

​ 實現電商平臺的基本操做spring

2.項目設計

3.建立項目 Product-Service

3.1 添加座標
<?xml version="1.0" encoding="UTF-8"?>
<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 https://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.13.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.luyi</groupId>
    <artifactId>springcloud-ego-product-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-ego-product-service</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</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>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
複製代碼
3.2 建立service接口
/** * 描述: 商品接口 */
@RequestMapping("/product")
public interface ProductService {

    //查詢全部商品
    @RequestMapping(value = "/findAll", method = RequestMethod.GET)
    public List<Product> findAll();
}
複製代碼
3.3 建立pojo類
/** * 描述: 商品實體 */
public class Product {

    private Integer id;
    private String name;

    public Product() {
    }

    public Product(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
複製代碼

4. 建立Product-Provider

4.1 添加座標
<?xml version="1.0" encoding="UTF-8"?>
<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 https://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.13.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.luyi</groupId>
    <artifactId>springcloud-ego-product-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-ego-product-provider</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</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>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

        <!--product service-->
        <dependency>
            <groupId>com.luyi</groupId>
            <artifactId>springcloud-ego-product-service</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
複製代碼
4.2 修改配置文件
spring.application.name=ego-product-provider
server.port=9001

#設置服務註冊中心地址,向全部註冊中心作註冊
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
複製代碼
4.3 編寫Controller
/** * Product-Provider服務 */
@RestController
public class ProductController implements ProductService {

    @Override
    public List<Product> findAll() {
        ArrayList<Product> list = new ArrayList<>();
        list.add(new Product(1, "電視"));
        list.add(new Product(2, "電腦"));
        list.add(new Product(3, "冰箱"));
        list.add(new Product(4, "手電筒"));
        return list;
    }
}
複製代碼
4.4 編寫SpringBoot的啓動類
@EnableEurekaClient
@SpringBootApplication
public class ProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }

}
複製代碼

5.建立Product-Consumer

5.1 添加座標
<?xml version="1.0" encoding="UTF-8"?>
<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 https://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.13.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.luyi</groupId>
    <artifactId>springcloud-ego-product-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-ego-product-consumer</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</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>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <!--添加feign的座標-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>

        <!--product service-->
        <dependency>
            <groupId>com.luyi</groupId>
            <artifactId>springcloud-ego-product-service</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
複製代碼
5.2 修改配置文件
spring.application.name=ego-product-consumer
server.port=9002

#設置服務註冊中心地址,向全部註冊中心作註冊
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
複製代碼
5.3 編寫controller
/** * Product-Consumer服務 */
@RestController
public class ProductController {

    @Autowired
    private ProductConsumerService consumerService;
    /** * Consumer中查詢全部商品的方法 * @return */
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public List<Product> list(){
        return consumerService.findAll();
    }
}
複製代碼
5.4 編寫service
//指定實現該接口的服務
@FeignClient(name = "ego-product-provider")
public interface ProductConsumerService extends ProductService {
}
複製代碼
5.5 修改啓動類
//添加以下兩個註解開啓對feign的支持
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

}
複製代碼

4、Feign的請求參數處理

1.單個參數處理

1.1 修改Product-Service項目,添加方法
//根據商品id查詢商品
@RequestMapping(value = "/getProductById", method = RequestMethod.GET)
//RequestParam必須指定參數
public Product getProductById(@RequestParam("id") Integer id);
複製代碼
1.2 修改Product-Provider
@Override
public Product getProductById(Integer id) {
    return new Product(id, "SpringCloud");
}
複製代碼
1.3 修改Product-Consumer
/** * Consumer中根據商品id查詢商品 */
@RequestMapping(value = "/get", method = RequestMethod.GET)
public Product getProduct(@RequestParam("id") Integer id){
    return consumerService.getProductById(id);
}
複製代碼

2.多個參數處理 方式1: Get提交方式

2.1 修改Product-Service
//添加商品,傳遞多個參數,get方式
@RequestMapping(value = "/add", method = RequestMethod.GET)
public Product addProduct(@RequestParam("id") Integer id, @RequestParam("name") String name);
複製代碼
2.2 修改Product-Provider
@Override
public Product addProduct(Integer id, String name) {
    return new Product(id, name);
}
複製代碼
2.3 修改Product-Consumer
/** * 商品添加,傳遞多個參數,get方式 */
@RequestMapping(value = "/add", method = RequestMethod.GET)
public Product addProduct(Product product){
    return consumerService.addProduct(product.getId(), product.getName());
}
複製代碼

3.多個參數處理 方式2: Post提交方式

3.1 修改Product-Service
//添加商品,傳遞多個參數,post方式
@RequestMapping(value = "/add2", method = RequestMethod.POST)
public Product addProduct2(@RequestBody Product product);
複製代碼
3.2 修改Product-Provider
@Override
public Product addProduct2(@RequestBody Product product) {
    return product;
}
複製代碼
3.3 修改Product-Consumer
/** * 商品添加,傳遞多個參數,post方式 */
@RequestMapping(value = "/add2", method = RequestMethod.GET)
public Product addProduct2(Product product){
    return consumerService.addProduct2(product);
}
複製代碼

5、Feign的性能優化

1.經過Gzip壓縮算法,提升網絡通信速度

1.1 gzip介紹

​ gzip原理:gzip是一種數據格式,採用deflate算法壓縮數據,gzip是一種流行的文件壓縮算法,應用十分普遍,尤爲是在Linux平臺。apache

​ gzip能力:當gzip壓縮到一個純文本文件時效果是很是明顯的,大約能夠減小70%以上的文件大小。json

​ gzip的做用:網絡數據通過壓縮後也就較低了網絡傳輸的字節數,最明顯的就是能夠提升網頁加載的速度。網頁加載速度加快的好處不言而喻,除了節省流量、改善用戶的瀏覽體驗外,另外一個潛在的好處就是gzip與搜索引擎的提取工具備着更好的關係。例如Google就能夠直接經過讀取gzip文件來比普通手工抓取更快的檢索網頁。瀏覽器

1.2 HTTP協議中關於壓縮傳輸的規定

​ 第一:客戶端向服務器請求中帶有:Accept-Encoding:gzip,deflate字段,向服務器表示,客戶端支持的壓縮格式(gzip或者deflate),若是不發送該消息頭,服務器是不會壓縮的。性能優化

​ 第二:服務端在收到請求以後,若是發現請求頭中含有Accept-Encoding字段,而且支持該類型的壓縮,就對響應報文壓縮以後返回給客戶端。而且攜帶Content-Encoding:gzip消息頭,表示響應報文是根據該格式壓縮過的。

​ 第三:客戶端接收請求以後,先判斷是否有Content-Encoding:消息頭,若是有,按改格式解壓報文,不然按正常報文處理。

2.編寫支持Gzip的壓縮案例

2.1 建立項目
2.2 修改配置文件
  • 只配置consumer經過feign到provider的請求與響應進行壓縮
#配置請求GZIP壓縮
feign.compression.request.enabled=true
#配置響應GZIP壓縮
feign.compression.respinse.enabled=true
#配置壓縮支持MIME TYPE
feign.compression.request.mime-types=text/xml,application/xml,application/json
#配置壓縮數據大小的最小閾值,默認2048
feign.compression.request.min-request-size=512
複製代碼
  • 對客戶端瀏覽器的請求以及consumer對provider的請求與響應作壓縮
#-------------spring boot gzip
#是否啓用壓縮
server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html,text/xml,type/plain
複製代碼

3.採用HTTP鏈接池,提高Feign的併發吞吐量

爲何http鏈接池能提升性能

3.1 http的背景原理

​ a.兩臺服務器創建http鏈接的過程是很複雜的過程,涉及到多個數據包的交換,而且也很消耗時間

​ b.Http鏈接須要三次握手四次揮手,開銷很大。這樣的開銷對於請求比較多但信息量又比較小的請求開銷更大。

3.2 優化解決方案

​ a.若是咱們直接採用http鏈接池,節約了大量三次握手四次揮手的時間,這樣能大大提高吞吐量。

​ b.feign的http客戶端支持3種框架:HttpURLConnection、HttpClient、okhttp,默認是HttpURLConnection。

​ c.傳統的HttpURLConnection是JDK自帶的,並不支持鏈接池,若是要實現鏈接池的機制。還須要本身來管理鏈接對象。對於網絡請求這種底層相對複雜的操做,若是有可用的其餘方案,也沒有必要本身去管理鏈接對象。

​ d.HttpClient相比於JDK自帶的HttpURLConnection,它封裝了訪問http的請求頭、參數、內容體、響應等等。它不只使發送http請求變得容易,並且也方便開發人員測試接口(基於HTTP協議的),即提升了開發的效率,也方便提升代碼的健壯性,另外高併發大量的請求的時候,仍是用鏈接池提升吞吐量。

4.將Feign的HTTP工具修改成HttpClient

4.1 建立項目
4.2 添加座標
<!--Apache HttpClient替換Feign原生httpURLConnection-->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>
<dependency>
    <groupId>com.netflix.feign</groupId>
    <artifactId>feign-httpclient</artifactId>
    <version>8.17.0</version>
</dependency>
複製代碼
4.3 修改配置文件,開啓HttpClient的使用
#啓用httpclient
feign.httpclient.enabled=true
複製代碼

注意:若是使用HttpClient做爲Feign做爲Feign的客戶端工具,那麼在定義接口上的註解時須要注意,若是傳遞的是一個自定義對象(對象會使用json類型來傳遞),須要添加指定類型

4.4 Product-Service
/** * 描述: 商品接口 */
@RequestMapping("/product")
public interface ProductService {

    //查詢全部商品
    @RequestMapping(value = "/findAll", method = RequestMethod.GET)
    public List<Product> findAll();

    //根據商品id查詢商品
    @RequestMapping(value = "/getProductById", method = RequestMethod.GET)
    public Product getProductById(@RequestParam("id") Integer id);

    //添加商品,傳遞多個參數,get方式
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public Product addProduct(@RequestParam("id") Integer id, @RequestParam("name") String name);


    //----------------------------------HttpClient------------------------------------
    //添加商品,傳遞多個參數,post方式
    @RequestMapping(value = "/add2", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    public Product addProduct2(@RequestBody Product product);

    //使用HttpClient工具添加商品,傳遞多個參數,基於Get方式
    @RequestMapping(value = "/add3", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE)
    public Product addProduct3(Product product);
}
複製代碼

6、查看微服務日誌中記錄每一個接口URL、狀態碼和耗時信息

1.建立項目

2.添加logback.xml文件

​ 將輸出日誌級別設置爲DEBUG

<!-- 日誌輸出級別 -->
<root level="DEBUG">
    <appender-ref ref="Stdout" />
    <appender-ref ref="RollingFile" />
</root>
複製代碼

3.在啓動類中添加一個方法

//添加以下兩個註解開啓對feign的支持
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {

    /** * NONE:不記錄任何信息,默認值 * BASIC:記錄請求url、請求方法、狀態碼和用時的時候使用 * HEADERS:在BASIC基礎上再記錄一些經常使用信息 * FULL:記錄請求和響應的全部信息 */
    @Bean
    public Logger.Level getLog(){
        return Logger.Level.FULL;
    }

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

}
複製代碼

7、配置Feign負載均衡請求超時時間

​ Feign的負載均衡底層用的就是Ribbon

1.修改配置文件,設置超時時間

1.1 全局配置
#全局配置
#請求鏈接的超時時間,默認爲1000ms
ribbon.ConnectTimeout=5000
#處理請求的超時時間
ribbon.ReadTimeout=5000
複製代碼
1.2 根據服務名稱進行局部超時配置
#局部配置
#對全部操做請求都進行重試
ego-product-provider.ribbon.OkToRetryOnAllOperations=true
#對當前實例的重試次數
ego-product-provider.ribbon.MaxAutoRetries=2
#切換實例的重試次數
ego-product-provider.ribbon.MaxAutoRetriesNextServer=0
#請求鏈接的超時時間
ego-product-provider.ribbon.ConnectTimeout=3000
#請求處理的超時時間
ego-product-provider.ribbon.ReadTimeout=3000
複製代碼
相關文章
相關標籤/搜索