中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

聲明式HTTP客戶端 - Spring Cloud OpenFeign

發布時間:2020-06-23 18:01:34 來源:網絡 閱讀:3089 作者:ZeroOne01 欄目:編程語言

Feign

什么是Feign:

  • Feign是Netflix開源的聲明式Http客戶端
  • 關于Feign的基本使用方式在微服務之間的通信的方式一文中介紹過,這里不再贅述

Feign的組成:
聲明式HTTP客戶端 - Spring Cloud OpenFeign

  • Feign.Builder:所有的FeignClient都是由Feign.Builder構建
  • Client:feign.Client.Default內部實際用的是HttpURLConnection,而LoadBalanceFeignClient默認情況下傳入的就是feign.Client.Default,只是加了個負載均衡的功能。相比于feign.Client.Default,LoadBalanceFeignClient支持傳入指定的Client
  • Contract:原生的Feign是不支持@GetMapping、@PostMapping...等SpringMVC注解的,Spring Cloud對其擴展后才支持

指定Feign的日志級別

Feign默認是不打印任何日志的,但在實際項目中接口調用出現問題需要調試代碼或需要查看某個接口調用所執行的耗時,那么第一時間就會想到查看Feign的日志,此時要如何去開啟Feign的日志呢?主要有兩種方式,通過代碼配置或通過配置文件配置。另外,配置生效范圍還分為局部配置和全局配置,我們先來介紹細粒度的局部配置。

需要注意的是,Feign的日志級別與Spring Boot不一樣,所以不能直接配置Spring Boot的日志級別去開啟。Feign的日志級別如下表:
聲明式HTTP客戶端 - Spring Cloud OpenFeign

1、局部配置 - 代碼配置;通過代碼配置有兩個主要的步驟,先在代碼定義相應的配置類,然后再到配置文件中配置FeignClient接口的日志級別。首先,定義Feign日志級別的配置類。代碼如下:

package com.zj.node.contentcenter.configuration;

import feign.Logger;
import org.springframework.context.annotation.Bean;

/**
 * @author 01
 * @date 2019-07-29
 **/
public class UserCenterFeignConfig {

    @Bean
    public Logger.Level level(){
        // 設置Feign的日志級別為FULL
        return Logger.Level.FULL;
    }
}

注:該類不要加上@Configuration注解,否則將會因為父子上下文掃描重疊而成為全局配置

由于不是做的全局配置,所以除此之外還需要在FeignClient接口中指定該配置類:

package com.zj.node.contentcenter.feignclient;

import com.zj.node.contentcenter.configuration.UserCenterFeignConfig;
import com.zj.node.contentcenter.domain.dto.user.UserDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(value = "user-center", configuration = UserCenterFeignConfig.class)
public interface UserCenterFeignClient {

    @GetMapping("/users/{id}")
    UserDTO findById(@PathVariable Integer id);
}

然后在配置文件中添加如下配置:

# 設置日志級別
logging:
  level:
    # 這里需要配置為debug,否則feign的日志級別配置不會生效
    com.zj.node.contentcenter.feignclient.UserCenterFeignClient: debug

配置完成后,啟動項目執行相應的調用代碼,控制臺輸出的日志如下:
聲明式HTTP客戶端 - Spring Cloud OpenFeign


2、局部配置 - 配置文件配置;這種配置方式就比較簡單,也是比較常用的方式,只需在配置文件中添加如下配置即可:

# 定義feign相關配置
feign:
  client:
    config:
      # 微服務名稱
      user-center:
        # 設置feign日志級別
        loggerLevel: full

# 設置日志級別
logging:
  level:
    # 這里需要配置為debug,否則feign的日志級別配置不會生效
    com.zj.node.contentcenter.feignclient.UserCenterFeignClient: debug

1、全局配置 - 代碼配置;同樣定義一個配置類:

public class GlobalFeignLoggerConfig {

    @Bean
    public Logger.Level level(){
        // 設置Feign的日志級別為FULL
        return Logger.Level.FULL;
    }
}

然后配置啟動類上的@EnableFeignClients注解的defaultConfiguration屬性,如下:

@EnableFeignClients(
        basePackages = "com.zj.node.contentcenter.feignclient",
        defaultConfiguration = GlobalFeignLoggerConfig.class
)

接著將配置文件中的日志配置從特定的類修改為包名,如下:

# 設置日志級別
logging:
  level:
    # 這里需要配置為debug,否則feign的日志級別配置不會生效
    com.zj.node.contentcenter.feignclient: debug

2、全局配置 - 配置文件配置;

# 定義feign相關配置
feign:
  client:
    config:
      # default表示為全局配置
      default:
        # 設置feign日志級別
        loggerLevel: full

# 設置日志級別
logging:
  level:
    # 這里需要配置為debug,否則feign的日志級別配置不會生效
    com.zj.node.contentcenter.feignclient: debug

Feign支持的配置項

由于使用代碼方式配置和使用配置文件配置所支持的配置項不同,所以分為兩類。

1、代碼方式所支持的配置項:

配置項 作用
Feign.Builder Feign的入口
Client Feign底層用什么http客戶端去請求
Contract 契約,注解支持
Encoder 編碼器,用于將對象轉換成Http請求消息體
Decoder ×××,將響應消息體轉換成對象
Logger 日志管理器
Logger.Level 指定日志級別
Retryer 指定重試策略
ErrorDecoder 指定異常×××
Request.Options 超時時間
Collection<RequestInterceptor> 請求攔截器
SetterFactory 用于設置Hystrix的配置屬性,Feign整合Hystrix才會用

2、配置文件所支持的配置項:
聲明式HTTP客戶端 - Spring Cloud OpenFeign

代碼配置 vs 配置文件配置:
聲明式HTTP客戶端 - Spring Cloud OpenFeign

  • 關于優先級:細粒度配置文件配置 > 細粒度代碼配置 > 全局配置文件配置 > 全局代碼配置

配置最佳實踐總結:

  • 盡量使用配置文件配置,配置文件滿足不了需求的情況下再考慮使用代碼配置
  • 在同一個微服務內盡量保持單一性,例如統一使用配置文件配置,盡量不要兩種方式混用,以免增加定位問題的復雜度

Feign的繼承

所謂Feign的繼承實際是為了服務之間能夠復用代碼,例如現在用戶中心服務有一個按id查詢用戶信息的接口如下:

@Slf4j
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    @GetMapping("/{id}")
    public User findById(@PathVariable Integer id) {
        log.info("get request. id is {}", id);
        return userService.findById(id);
    }
}

若我想在內容中心服務通過Feign調用該接口,就需要新建一個interface,并編寫如下代碼:

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

    @GetMapping("/users/{id}")
    UserDTO findById(@PathVariable Integer id);
}

可以看到,方法的定義實際上是一樣的,所以這時候就可以利用Feign的繼承特性復用這種代碼。首先需要創建一個單獨的項目或maven模塊,因為這樣才能通過添加maven依賴的方式引入到不同的項目中。這里暫且稱為api模塊吧,在api模塊中定義一個這樣的接口,代碼如下:

@RequestMapping("/users")
public interface UserApi {

    @GetMapping("/{id}")
    User findById(@PathVariable Integer id);
}

然后在用戶中心服務中添加api模塊的依賴,接著實現UserApi接口,改寫之前的UserController如下:

@Slf4j
@RestController
@RequiredArgsConstructor
public class UserController implements UserApi {

    private final UserService userService;

    @Override
    public User findById(@PathVariable Integer id) {
        log.info("get request. id is {}", id);
        return userService.findById(id);
    }
}

在內容中心服務中也添加api模塊的依賴,改寫之前的UserCenterFeignClient代碼,讓其繼承UserApi,代碼如下:

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

可以看到,繼承了UserApi后,此時不需要再定義與目標接口相同的方法了,復用了上級接口的代碼,這就是所謂Feign的繼承。

其實關于這種使用方式存在許多爭議,我們來看看官方怎么說:

It is generally not advisable to share an interface between a server and a client. It introduces tight coupling, and also actually doesn’t work with Spring MVC in its current form (method parameter mapping is not inherited).

大致翻譯如下:

通常不建議在服務提供者(server)和服務消費者(client)之間共享接口,因為這種方式引入了緊耦合,并且實際上在當前形式下也不適用于Spring MVC(方法參數映射不會被繼承)

  • 關于方法參數映射不會被繼承:在上面的代碼示例中可以看到,實現UserApi的UserController方法參數上,依舊需要寫MVC相關的注解,因為這些注解是不會被繼承的。簡單來說就是這類注解得寫在實現類的方法參數上才會生效,而對于團隊中對此不甚熟悉的開發人員來說也會造成一定的”迷惑“

官網文檔地址如下:

https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#spring-cloud-feign-inheritance

關于繼承特性的爭議:

  • 官方觀點:不建議使用
    • 理由上面已說明
  • 業界觀點:很多公司使用
    • 理由1:代碼可復用;面向契約
    • 理由2:在業務需求變更比較頻繁的情況,無需修改太多的代碼

如何抉擇:

根據項目情況權衡利弊即可,若需要這種特性帶來的好處又可以承受緊耦合帶來的負面影響,那么就選擇使用該特性,否則就不要使用


Feign發送多參數GET請求的坑

使用過Spring MVC的都知道,當一個GET接口有多個請求參數時可以使用對象來接收。例如用戶服務中,有這樣一個接口如下:

@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping("/query")
    public User query(User user) {
        return user;
    }
}

使用postman發送如下請求是可以正常接收并響應的:
聲明式HTTP客戶端 - Spring Cloud OpenFeign

所以在另一個服務中使用Feign調用這種類型的接口時,我們很自然而然的就會寫成如下形式:

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

    @GetMapping("/users/query")
    UserDTO query(UserDTO userDTO);
}

實際上這種使用Feign發送多參數GET請求的方式是會有坑的,因為將多參數包裝成對象時,Feign在底層會將其轉換為POST請求,并把對象序列化塞到http body中,所以就會由于不支持該請求方法而報405錯誤。

關于這個坑我們做個實驗來驗證一下,在內容中心服務中,定義一個接口如下:

@RestController
@RequestMapping("/shares")
@RequiredArgsConstructor
public class ShareController {

    private final UserCenterFeignClient userCenterFeignClient;

    @GetMapping("/queryUser")
    public UserDTO queryUser(UserDTO userDTO){
        return userCenterFeignClient.query(userDTO);
    }
}

然后通過postman進行請求,可以看到直接報405錯誤了:
聲明式HTTP客戶端 - Spring Cloud OpenFeign

此時用戶服務的控制臺中,輸出了如下日志信息:

Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]

那么我們要如何去解決這個坑呢?最顯而易見的方式就是不將參數包裝成對象,而是拆解開來使用@RequestParam一個個寫上去。然而這種方式有個很明顯的弊端,如果有很多參數的時候,一個個寫就比較累,而且代碼也不好看。在這種“走投無路”的情況下,就會想著要不就不用GET了,換成POST吧。雖然這種方法也可行,但是卻違背了RESTful的規范。

那有沒有一個完美的解決方案呢?答案是有的,那就是使用@SpringQueryMap注解,該注解相當于feign.QueryMap,目的是將對象轉換為GET參數。那么我們就來試試看吧,修改UserCenterFeignClient代碼如下:

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

    @GetMapping("/users/query")
    UserDTO query(@SpringQueryMap UserDTO userDTO);
}

注:該注解在spring-cloud-starter-openfeign: 2.1.0及之后的版本才開始支持的,之前的版本只能使用其他方式解決該問題。之所以會有這個坑,也是因為原生Feign的體系讓Spring Cloud無法封裝得與Spring MVC完全一致的編程體驗

修改完代碼后重啟項目,再次使用postman請求就沒有報錯了:
聲明式HTTP客戶端 - Spring Cloud OpenFeign


Feign脫離Ribbon使用

我們都知道Feign內部整合了Ribbon,所以才能有負載均衡功能及從服務發現組件獲取服務實例的調用地址功能。那么如果需要調用一個沒有注冊到服務發現組件上的服務或地址,即脫離Ribbon去使用Feign的話,要如何做呢?非常簡單,只需要配置一下@FeignClient注解的url屬性即可。如下示例:

// name是必須配置的,否則項目都無法啟動,url屬性通常是配置basic地址
@FeignClient(name = "baidu", url = "https://www.baidu.com")
public interface TestFeignClient {

    @GetMapping
    String index();
}

然后定義一個接口測試一下:

@RestController
@RequiredArgsConstructor
public class TestController {

    private final TestFeignClient feignClient;

    @GetMapping("/baidu")
    public String baiduIndex() {
        return feignClient.index();
    }
}

啟動項目,瀏覽器訪問如下:
聲明式HTTP客戶端 - Spring Cloud OpenFeign


Feign性能優化

RestTemplate VS Feign:
聲明式HTTP客戶端 - Spring Cloud OpenFeign

從上圖中可以看到,Feign只在性能和靈活性上輸給了RestTemplate,至于靈活性官方也說了無論如何優化也不可能像RestTemplate一樣,而性能則是可以進一步提高的。

默認情況下Feign的性能在RestTemplate的50%左右,雖然項目的瓶頸一般不會出現在Feign上,但如果能讓Feign的性能更好一些,也只是有利無害,所以本小節簡單談談Feign的性能優化。

默認情況下Feign底層是使用HttpURLConnection發送請求的,眾所周知HttpURLConnection是沒有使用連接池的,所以可以針對這點進行優化。例如,將底層的http請求客戶端為更換為Apache的HttpClient或者OkHttp等使用了連接池的http客戶端,據測試使用了連接池后可以提升15%左右的性能。

另一個優化的點就是設置合理的日志級別,之前已經介紹過日志級別的配置方式了,所以這里僅演示如何為Feign更換其他的http請求客戶端及配置連接池。

這里先以HttpClient為例,第一步加依賴:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

第二步,添加配置:

feign:
  httpclient:
    # 讓feign啟用httpclient作為發送http請求的客戶端
    enabled: true
    # 最大連接數
    max-connections: 200
    # 單個路徑的最大連接數
    max-connections-per-route: 50    

使用okhttp也是一樣的,第一步加依賴:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

第二步,添加配置:

feign:
  okhttp:
    # 讓feign啟用okhttp作為發送http請求的客戶端
    enabled: true
    # 最大連接數
    max-connections: 200
    # 單個路徑的最大連接數
    max-connections-per-route: 50
向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

灌阳县| 红桥区| 华蓥市| 日土县| 巴林右旗| 漳州市| 会昌县| 长垣县| 嵊州市| 蛟河市| 历史| 花垣县| 永康市| 桃园市| 朔州市| 德昌县| 绥滨县| 潜江市| 尚义县| 横峰县| 海丰县| 米林县| 临洮县| 南投市| 靖宇县| 静海县| 鄂托克前旗| 盖州市| 白银市| 衡水市| 织金县| 巫溪县| 灌阳县| 三门县| 财经| 丹寨县| 武汉市| 江源县| 资中县| 铁力市| 开江县|