Spring Cloud之Feign


資料來源: <<重新定義Spring Cloud>>以及博客和官網

源碼地址:https://gitee.com/08081/hello-springcloud

什么是feign ?

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

1. 什么是聲明式呢?

  • 聲明式調用就像調用本地方法一樣調用遠程方法,無感知遠程http請求
  • Spring Cloud的聲明式調用,可以做到使用HTTP請求遠程服務時就像調用本地方法一樣的體驗,開發者完全感知不到這是遠程調用,更加不知道這是一個http請求
  • 像dubbo一樣,consumer直接調用接口方法調用provider,而不需要通過常規的httpclient構造在解析返回數據
  • 解決了.開發者調用遠程和調用本地一樣,無需關心遠程交互的細節,更無需關注分布式環境

2. 工作原理

  • 在開發微服務應用時,我們會在主程序入口添加@EnableFeignClients注解開啟對FeginClient掃描加載處理,
  • 當程序啟動時,會進行掃描,掃描所有@FeginClients的注解類,並將這些信息,注入到Spring IOC容器中. 當定義的fegin接口中的方法被調用時,通過JDK代理的方式,來生成具體的RequestTemplate,當生成代理時,Fegin會為每一個接口方法創建一個RequestTemplate對象,該對象封裝了HTTP請求需要的全部信息,如請求參數,請求方法等信息都是在這個過程中確定的.
  • 然后由RequestTemplate生成request,然后把request交給client去處理,這里說的client 可以是jdk中的urlConnection apache 的httpclient 最后client被封裝到LoadBalanceClient類,這個類接口ribbon負載均衡發起服務之間的調用

入門案例

我們在父工程下創建ch4-fegin 這個父工程在他下面創建 3個模塊 

1. book-api: 主要作為一個接口給別的服務依賴,包含 實體對象,和api

2. book-service: 主要是作為book-api 的實現類模塊

3.book-consumer: 調用book服務的類

下面是代碼:

先看book-api的代碼

實體類:

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Book {

    private int id;
    private String name;

}

api:

package com.xiaodao.api;

import com.xiaodao.entity.Book;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@RequestMapping("/book")
public interface BookApi {

    @GetMapping(value = "list")
    List<Book> findList();
}

pom:也是空的pom

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ch4-fegin</artifactId>
        <groupId>com.xiaodao</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ch4-book-api</artifactId>


</project>

ch4-book-service的代碼:

pom:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ch4-fegin</artifactId>
        <groupId>com.xiaodao</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ch4-book-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.xiaodao</groupId>
            <artifactId>ch4-book-api</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
</project

bootstrap.yml:

spring:
  application:
    name: book-service
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/eureka/
  instance:
    prefer-ip-address: true
server:
  port: 8000

bookserviceImpl:

package com.xiaodao.service;

import com.xiaodao.api.BookApi;
import com.xiaodao.entity.Book;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.ValueConstants;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@RestController
@RequestMapping("book")
public class BookServiceImpl implements BookApi {
    @GetMapping(value = "list")
    @Override
    public List<Book> findList() {

        Book book =Book.builder().id(1).name("第一本書").build();
        System.out.println(book);
       List<Book> list =  new  CopyOnWriteArrayList<>();
       list.add(book);
       return list;
    }
}
View Code

bookApplicatiton

package com.xiaodao;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@SpringBootApplication
public class BookApplication {
    public static void main(String[] args) {
        SpringApplication.run(BookApplication.class,args);
    }
}
View Code

ch4-book-consumer 的代碼:

pom:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ch4-fegin</artifactId>
        <groupId>com.xiaodao</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ch4-book-consumer</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.xiaodao</groupId>
            <artifactId>ch4-book-api</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

</project>
View Code

bootstrap.yml

spring:
  application:
    name: book-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/eureka/
  instance:
    prefer-ip-address: true
server:
  port: 8001

service:就是繼承了book-api service的接口 

name 是 book-service 的實例名

package com.xiaodao.service;

import com.xiaodao.api.BookApi;
import org.springframework.cloud.openfeign.FeignClient;

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@FeignClient(name = "book-service")
public interface BookService extends BookApi {
}

controller.調用service.->通過Fegin->book-service

package com.xiaodao.controller;

import com.xiaodao.entity.Book;
import com.xiaodao.service.BookService;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@RestController
@RequestMapping("/book")
@AllArgsConstructor
public class BookController {


    private BookService bookService;

    @GetMapping("/findList")
    public List<Book> findList(){
      return   bookService.findList();
    }
}

最后我們啟動eurekasever  book-service book-consumer 我們可以訪問eurekaService

 

我們去調用book-consumer里的controller方法,看看能不能調用book-service的實現類:

 以上就是一個簡單的hello word 實現的方法.

傳參數

1.單個參數時候沒有問題的

2.多個參數用post方法 (@requestBody Book book) feign是不支持 get方法傳遞實體對象的

3.如果想用get方式傳實體對象需要將feign默認的URLConnection連接換成httpClient

fegin 性能優化

gzip壓縮

  1. gzip介紹:gzip是一種數據格式,采用defalte算法壓縮data gzip是一種流行的文件壓縮算法,應用十分廣泛,尤其是在liunx平台
  2. gzip能力: 當gzip壓縮一個純文本文件時,效果是非常明顯,大約可以減少70%以上的文件大小
  3. gzip作用: 網絡數據通過壓縮之后實際上降低了網絡傳輸的字節數,最明顯的好處時候加快網頁的加載速度,網頁加載速度加快的好處不言而喻,除了節省流量以外,改善用戶體驗外,另一個潛在的好處是Gzip與搜索引擎的抓取工具有着更好的關系,例如:Google 就可以通過讀取Gzip文件來比普通手工抓取 更快的檢索網頁

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

1. 客戶端向服務器請求中帶有:Accept-Encoding:Gzip,deflate字段,向服務器表示,客戶端支持的壓縮格式(gzip或者 deflate),如果不發送消息頭,服務器是不會壓縮的

2. 服務端收到請求之后,如果發現請求中含有Accept-Encoding字段,並且支持該類型的壓縮,就對響應報文壓縮之后在返回客戶端,並且攜帶Content-Encoding:gzip消息頭,表示報文是經過壓縮的

3. 客戶端接到服務器的響應之后,先判斷是否有Content-Encoding消息頭,如果有,按格式解壓報文,否者按正常處理

我們在baidu搜索下發送一個消息

 

看到是百度服務器是經過壓縮的

然后我們在看下我們剛才的代碼.

可以看出是沒有經過壓縮的.

那我們使用fegin怎么做壓縮呢?

spring:
  application:
    name: book-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/eureka/
  instance:
    prefer-ip-address: true
server:
  port: 8001
### Feign 配置
feign:
  compression:
    request:
      # 開啟請求壓縮
      enabled: true
      # 配置壓縮的 MIME TYPE
      mime-types: text/xml,application/xml,application/json
      # 配置壓縮數據大小的下限
      min-request-size: 2048
    response:
      # 開啟響應壓縮
      enabled: true

這里有坑,當我在book-consumer 配置了壓縮的時候,我發現我們請求是配置了gzip壓縮了但是服務器並沒有壓縮.為什么呢?是當我們使用瀏覽器訪問的時候沒有壓縮.

當我們在配置spring boot壓縮之后:在看控制台就顯示服務器也壓縮過了

spring:
  application:
    name: book-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/eureka/
  instance:
    prefer-ip-address: true
server:
  port: 8001
  #配置spring boot 壓縮
  compression:
    enabled: true
#    mime-types:
### Feign 配置
feign:
  compression:
    request:
      # 開啟請求壓縮
      enabled: true
      # 配置壓縮的 MIME TYPE
      mime-types: text/xml,application/xml,application/json
      # 配置壓縮數據大小的下限
      min-request-size: 2048
    response:
      # 開啟響應壓縮
      enabled: true
View Code

 

也可以不寫fegin的壓縮,直接 就用springboot的壓縮就好了.

 HTTP連接池優化

為什么http連接池能提升性能?

http的背景原理

1.倆台服務器建立http的過程是很富在的工程,涉及到了多個數據包的交換,並且也耗費時間

2. http連接需要3次握手4次分手開銷很大,這一開銷對於大量比較小的http消息來說更大

優化解決方案

1.如果我們采用http連接池,節約了大量的3次握手4次分手,這樣能大大的提高性能

2.fegin的http客戶端支持3種框架 : HttpURLConnection httpclient okhttp 默認是HTTPURLConnection.

3.傳統的HTTPURLConnection是JDK自帶的,並不支持線程池,如果要實現連接池的機制,還需要自己管理連接對象,對於網絡請求這種底層相對復雜的操作,如果有可用的方案,也沒有必要自己去實現

4.HttpClient相比於JDK自帶的URLConnection,它封裝了訪問http的請求頭,參數,內容體響應等等,它不僅是客戶端發送http請求變的容易,而且方便開發人員測試接口(基於http協議), 也提高的效率,也方便提高代碼的健壯性,另外高並發大量請求網絡的時候,還是使用"連接池"提升吞吐量的.

使用httpClient連接池

1.在book-consumer 的pom中:加入

     <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.5</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
            <version>9.5.1</version>
        </dependency>

2.在bootstrap.yml中加入

spring:
  application:
    name: book-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/eureka/
  instance:
    prefer-ip-address: true
server:
  port: 8001
  #配置spring boot 壓縮
  compression:
    enabled: true
#    mime-types:
### Feign 配置
feign:
  compression:
    request:
      # 開啟請求壓縮
      enabled: true
      # 配置壓縮的 MIME TYPE
      mime-types: text/xml,application/xml,application/json
      # 配置壓縮數據大小的下限
      min-request-size: 2048
    response:
      # 開啟響應壓縮
      enabled: true
  #啟動httpclient
  httpclient:
    enabled: true
View Code

 日志輸出:

bootstrap.yml

logging:
  level:
    #這個是使用feign的那個接口類
    com:
      xiaodao:
        service:
          BookService: DEBUG
@Configuration
public class LoggerConfig {
    @Bean
    public Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

 Feign的優化配置

feign的負載均衡,是使用ribbon,就需要我們對ribbon的配置進行配置.

ribbon的配置分為全局配置,和局部配置.

先來看全局配置:

在consumer的bootstrap.yml中加入:

#全局配置
ribbon:
  #請求連接時間
  ConnectTimeout: 5000
  #請求處理時間
  ReadTimeout: 5000

在book-service的controller讓他睡眠:

@RestController
@RequestMapping("book")
public class BookServiceImpl implements BookApi {
    @GetMapping(value = "list")
    @Override
    public List<Book> findList() {
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Book book =Book.builder().id(1).name("第一本書").build();
        System.out.println(book);
       List<Book> list =  new  CopyOnWriteArrayList<>();
       list.add(book);
       return list;
    }


}

這個時候就超時了.

局部配置

但是我們一般不用全局配置,因為你的cosumer一般情況會調用很多provider,他們的情況不一.

#請求連接的超時時間
book-service.ribbon.ConnectTimeout=3000

這里有很多配置,有重試等可以去官網查看.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM