學習 spring-cloud-aibaba第四篇,聲明式HTTP通訊客戶端 Feign


前情提要:nacos上註冊了content-centeruser-center兩個服務,content-center使用Feign調用user-center服務,使用Ribbon做負載均衡

1.RestTemplate VS Feign

在這裏插入圖片描述

  • Feign讓我們的代碼可讀性、可維護性極佳,這是決勝點
  • Feign 的唯一短板性能只有RestTemplate的一半,但是也有優化提升的空間
  • 所以儘量使用 Feign
  • 然而事無絕對,合理選擇

2.Feign的組成

在這裏插入圖片描述
我們需要關注的幾個部分

  • Client 可以自定義請求Client,提高性能
  • Logger 默認是不打印日誌,所以這裏需要配置
  • RequestInterceptor 攔截器,很有用,例如給每個請求加Header

3.項目添加Feign

content-center 項目添加Feign組件,依然三板斧

3.1 加依賴

pom.xml

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

3.2 加註解

啓動類ContentApplication添加註解@EnableFeignClients

@SpringBootApplication
@MapperScan("com.zengchen.content.mapper")
@EnableFeignClients
public class ContentApplication {

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

}

3.3 寫配置

沒有必須寫的配置

4.項目使用Feign

4.1 聲明 FeignClient(UserCenterFeignClient)

新建個包feignclient下面新建接口UserCenterFeignClient

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "user-center")
public interface UserCenterFeignClient{

    /**
     * FeignClient的name + GetMapping的value
     * 相當於 http://user-center/reciteHis/testAno
     * 和RestTemplate裏寫的url一模一樣
     * @return Page
     */
    @GetMapping(value = "/reciteHis/testAno")
    Page memberRctHisAno();
}

4.2 使用UserCenterFeignClient

重構Controller裏的方法,代碼很簡潔,並且從client接口裏知曉每個方法是做什麼的,可讀性變強了

    @Autowired
    private UserCenterFeignClient userCenterFeignClient;

    @GetMapping(value = "testRibbon")
    public Object testRibbon() {
          //1. 未使用Ribbon時
//        List<ServiceInstance> instances = discoveryClient.getInstances("user-center");
//        String targetUrl = instances.stream()
//                .map(instance -> instance.getUri().toString() + "/reciteHis/testAno")
//                .findFirst()
//                .orElseThrow(() -> new IllegalArgumentException("當前沒有實例!"));

          // 2.Ribbon + restTemplate
//        String targetUrl= "http://user-center/reciteHis/testAno";
//        Page page = restTemplate.getForObject(targetUrl, Page.class);

        // 3.Feign
        Page page = userCenterFeignClient.memberRctHisAno();
        return ResponseVO.success(page);
    }

4.3 測試

請求content-center的http://localhost:8082/reciteHis/testAno。使用Feign成功
在這裏插入圖片描述

5.Feign的日誌級別

開發環境推薦使用FULL,生產環境推薦BASIC
在這裏插入圖片描述

5.1 java代碼方式自定義日誌級別

  • 設置UserCenterFeignClient的項目(不是Feign的日誌,注意這兩個日誌的概念區別)日誌級別爲debug
    只有這個文件的日誌級別設置爲debug,Feign的日誌才能打印出來,這是先決條件
logging:
  file: D://springlog/content/log.log
  level:
    com.zengchen.content.feignclient.UserCenterFeignClient: debug
  • 新建UserCenterFeignClientConfiguration
           和Ribbonjava代碼配置一樣,寫個配置類,返回日誌級別。注意這裏的Logger,得是feign包下面的Logger,別引用錯了
           如果你加了@Configuration,也會產生父子上下文問題,變成了Feign的全局配置
           所以,要麼別加@Configuration註解,要麼把這個配置類放到一個啓動類掃描不到的包裏,like ribbon的配置類
           這裏推薦不加@Configuration註解的方式,因爲簡單啊。
           那麼ribbon的配置類問什麼不採用這種不加註解的方式呢?因爲ribbon的配置類必須加@Configuration,不加不行啊,所以ribbon沒辦法像feign一樣
import feign.Logger;
import org.springframework.context.annotation.Bean;
//@Configuration
public class UserCenterFeignClientConfiguration {

    @Bean
    public Logger.Level loggerLevel(){
        //全日誌
        return Logger.Level.FULL;
    }
}
  • UserCenterFeignClient使用UserCenterFeignClientConfiguration
    configuration = UserCenterFeignClientConfiguration.class
@FeignClient(name = "user-center",configuration = UserCenterFeignClientConfiguration.class)
public interface UserCenterFeignClient {

    /**
     * FeignClient的name + GetMapping的value
     * 相當於 http://user-center/reciteHis/testAno
     * 和RestTemplate裏寫的url一模一樣
     * @return Page
     */
    @GetMapping(value = "/reciteHis/testAno")
    Page memberRctHisAno();
}

5.2 屬性配置方式自定義日誌級別

  • 先把UserCenterFeignClient的註解註釋掉
//@FeignClient(name = "user-center",configuration = UserCenterFeignClientConfiguration.class)
@FeignClient(name = "user-center")
public interface UserCenterFeignClient {

    /**
     * FeignClient的name + GetMapping的value
     * 相當於 http://user-center/reciteHis/testAno
     * 和RestTemplate裏寫的url一模一樣
     * @return Page
     */
    @GetMapping(value = "/reciteHis/testAno")
    Page memberRctHisAno();
}
  • 寫配置
logging:
  file: D://springlog/content/log.log
  level:
    com.zengchen.content.feignclient.UserCenterFeignClient: debug
feign:
  client:
    config:
      # 想要調用的微服務的名稱
      user-center:
        loggerLevel: basic

6.Feign的全局配置

上小節的日誌配置,都是隻針對於user-center服務的起作用,如果content-center還要調用比如,短信服務,廣告服務等等,就看不到feign的日誌了,因爲feign默認不打印日誌,全局配置就是content-center調用其它所有的服務都起作用的配置

6.1 java代碼方式

  • 利用父子上下文ComponentScan的bug
    這雖然是一種方式,但是這是一種病態的方式,強烈不建議使用
  • 啓動類@EnableFeignClientsdefaultConfiguration屬性
    註釋application.yml裏的配置
#feign:
#  client:
#    config:
#      # 想要調用的微服務的名稱
#      user-center:
#        loggerLevel: basic

給@EnableFeignClients添加
defaultConfiguration = UserCenterFeignClientConfiguration.class屬性,這樣UserCenterFeignClientConfiguration就變成了全局的配置,不再僅限於調用user-center的時候

@SpringBootApplication
@MapperScan("com.zengchen.content.mapper")
@EnableFeignClients(defaultConfiguration = UserCenterFeignClientConfiguration.class)
//@EnableFeignClients(basePackages = "com.zengchen.user.client")
public class ContentApplication {

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

}

6.2 屬性配置方式

  • 註釋java代碼全局配置
@SpringBootApplication
@MapperScan("com.zengchen.content.mapper")
@EnableFeignClients//(defaultConfiguration = UserCenterFeignClientConfiguration.class)
//@EnableFeignClients(basePackages = "com.zengchen.user.client")
public class ContentApplication {

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

}
  • 修改屬性配置
    把原來的user-center改成default就行
feign:
  client:
    config:
      # 全局配置
      default:
        loggerLevel: basic

6.3 java代碼方式 vs 屬性配置方式

這兩種方式所支持的配置項是不同的 !!

  • java代碼方式支持的配置項
    在這裏插入圖片描述
  • 屬性配置方式支持的配置項
    在這裏插入圖片描述

7.配置實踐總結

7.1 Ribbon配置 vs Feign配置

在這裏插入圖片描述

7.2 Feign代碼方式 vs 屬性方式

在這裏插入圖片描述
我測試了一下,Ribbon代碼方式比屬性方式優先級高,Feign代碼方式比屬性方式優先級低

7.3 最佳配置搭配推薦

  • 儘量使用屬性配置,屬性配置實現不了再考慮用代碼配置
  • 同一個微服務儘量保持配置方式單一性,不要兩種配置方式混用,會增加定位代碼問題的複雜性

8.多參數請求構造

參考大目老師手記:https://www.imooc.com/article/289000

9.Feign請求非註冊服務的接口

這種方式不會使用到Ribbon,所以也叫Feign脫離Ribbon的使用方式

  • 重新定義一個 TestImoocFeignClient
    @FeignClient(name = “getImooc”,url = “www.baidu.com”),name隨便自己定義,url就是你想請求的鏈接。關鍵就是這個url參數
//@FeignClient(name = "getImooc",url = "localhost:8082/reciteHis/1")
//@FeignClient(name = "getImooc",url = "www.sogou.com")
//@FeignClient(name = "getImooc",url = "www.imooc.com")
@FeignClient(name = "getImooc",url = "www.baidu.com")
public interface TestImoocFeignClient {

    @GetMapping(value = "")
    String index();
}
  • 新建 TestImoocController 使用TestImoocFeignClient裏的index方法
@RestController
public class TestImoocController {

    @Autowired
    TestImoocFeignClient testImoocFeignClient;

    @GetMapping(value = "getImooc")
    public String imoocIndex(){
        return this.testImoocFeignClient.index();
    }
}
@FeignClient(name = "getImooc",url = "localhost:8082/reciteHis/1")

10 Feign性能優化

Feign的性能只有RestTemplate50%,但是不用爲Feign擔心,一般項目的瓶頸不會發生在Feign上,優化僅是爲了更好!

10.1 優化原理

第二節 Feign的組成裏有Client組件,就是優化這個Client

  • 不和Ribbon配合使用的時候,Client 是默認的Feign.client.default ,這個default裏使用的是HttpURLConnection,這個HttpURLConnection,不支持連接池,所以性能不高
  • 和Ribbon配合使用的時候,Client 使用的是LoadBalancerFeignClient,支持代理模式,默認的也是Feign.client.default
    在這裏插入圖片描述
    從上圖可以看出,還有兩個構造client的類,一個是HttpClient,一個是OKHttp。優化就是用它們替換default

10.2 使用HttpClient優化

  • 引入依賴
		<dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
        </dependency>
  • 寫配置
feign:
  client:
    config:
      # 全局配置
      default:
        loggerLevel: basic
  httpclient:
    # 讓feign使用apache httpclient做請求,而不是默認的httpurlconnection
    enabled: true
    # feign 最大連接數
    max-connections: 200
    # feign 單個路徑的最大連接數
    max-connections-per-route: 50

10.3 使用OKHttp優化

  • 引入依賴
		<dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
            <!--<version>10.3.0</version>-->
        </dependency>
  • 寫配置
feign:
  client:
    config:
      # 全局配置
      default:
        loggerLevel: basic
  okhttp:
    enabled: true
  httpclient:
    # 讓feign使用apache httpclient做請求,而不是默認的httpurlconnection
#    enabled: true
    # feign 最大連接數
    max-connections: 200
    # feign 單個路徑的最大連接數
    max-connections-per-route: 50

max-connectionsmax-connections-per-route使用的是httpclient裏面的

11 Feign常見問題總結

參考大幕老師手記:https://www.imooc.com/article/289005

12 Feign的繼承特性

https://cloud.spring.io/spring-cloud-openfeign/reference/html/#spring-cloud-feign-inheritance
在這裏插入圖片描述

12.1 user-center項目拆分成三個模塊

這個可以參照我的另一篇博文 :
https://blog.csdn.net/hantangduhey/article/details/99306490

12.2 未完。。。

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