微服務改造之Openfeign的強化插件

在接觸 Spring Cloud 這套框架之前,筆者使用的一直是Dubbo。在轉型到Spring Cloud 後,發現了一個很鬱悶的問題。Spring Cloud 中的 Openfeign,相比於 Dubbo 對單個方法和單個接口(Interface)設置RPC超時時間,支持的不是特別理想。我查閱了一些資料和博客發現,現有的 Feign 只支持RPC超時設置的方式主要有以下幾種

第一種配置默認的連接超時和讀取超時,該配置全局生效。

feign.client.config.default.connectTimeout=2000
feign.client.config.default.readTimeout=5000

第二種是設置服務級別的連接超時和讀取超時,該配置對這個服務的全部接口生效。

# 單個服務 連接超時時間
feign.client.config.{這裡填寫服務名稱}.connectTimeout = 5000
# 單個服務 讀超時時間
feign.client.config.{這裡填寫服務名稱}.readTimeout = 10000

以上兩種的粒度都非常粗,所以使用起來並不是很理想。下面要介紹的方案避免了這個粗粒度的問題。

第三種是藉助於Hystrix實現方法級別的超時時間設置:

#首先是開啟 Hystrix
feign.hystrix.enabled = true

# 全局設置超時
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 30000

#服務級別設置超時
hystrix.command.{這裡填寫服務名稱}.execution.isolation.thread.timeoutInMilliseconds: 30000

要對以下接口進行設置:

public interface MemberService {
    @RequestMapping("/getUsers")
    List<User> getUsers(String name);
}
# 方法級別設置超時
hystrix.command.MemberService#getUser(String).execution.isolation.thread.timeoutInMilliseconds = 3000

分析一下一下幾種方法的優劣:

首先以上幾種方法都是可以使用的沒有問題,只是用起來不是特別爽快,相比於Dubbo總感覺缺點東西。第一種和第二種粒度太大,如果只是有一個方法需要很長的時間,如果只是為了這個方法,而將這個服務下所有的方法都設置成同樣長的超時時間,顯然是不太合適的。而第三種方法又要開啟Hystrix,說句老實話,Hystrix現在新的項目使用的越來越少,就像筆者現在使用的是Sentinel。而且Hystrix的超時設置比較奇特用並不是我們所理解的Rpc 連接超時和讀取超時,而是執行超時時間。如果有興趣的同學可以自己去了解一下,因為篇幅有限這裡就不展開講了。

各位看官看到這裡是不是要開始吐槽了,講了大半天了,講的都是大家知道的,一點乾貨都沒有,說好的強化插件呢???

 

各位大大不要着急,下面就要開始介紹Feign的強化插件,集成了連接超時,讀取超時和重試。分為服務提供方的註解形式和服務消費方的配置形式。該插件的的作用在於補全openfeign客戶端在使用的默認代理和 Sentinel 代理 的過程中無法自定義單個接口或者單個方法級別的超時時間與重試的插件。

本插件的優先級為:

服務消費者method配置 > 服務提供者method > 服務消費者interface級配置 > 服務提供者interface級配置 > 服務消費者全局配置
注意:是否允許重試以服務提供者註解標識為最高優先級,只有服務提供方註解顯示的表明可以重試,才能執行重試。如果註解沒有,默認不可重試。

服務提供方 maven 引用:

            <dependency>
                <groupId>org.example</groupId>
                <artifactId>openfeign-strengthen-plugin-annotation</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>

java 代碼書寫方式(服務提供方使用註解形式):

 /**
             * 註解放在 interface 上,則該 interface 下所有的方法 共享該配置
             */
            @RpcInfo(connectTimeout = 2,connectTimeoutUnit = TimeUnit.SECONDS, readTimeout = 1, readTimeoutUnit = TimeUnit.SECONDS, followRedirects = false)
            public interface MemberService {
            
                /**
                 * 註解放在 method 上
                 * 則該 method  RPC調用 時使用該配置。
                 * 方法上的配置優先級大於 interface 級別,
                 * 同時存在時,以 method 上的為準
                 * connectTimeout 連接超時時間
                 * connectTimeoutUnit 連接超時時間的單位 默認為毫秒
                 * readTimeout 讀取超時時間
                 * readTimeoutUnit 讀取超時時間單位 默認為毫秒
                 * followRedirects 是否重定向 默認為否
                 * maxAutoRetriesNextServer 切換實例的重試次數 默認為 0 次 禁止重試
                 * maxAutoRetries 對當前實例的重試次數 默認為 0 次 禁止重試
                 * isAllowedRetry 是否允許重試 默認為 false 不允許
                 */
                @RequestMapping("/getUsers")
                @RpcInfo(connectTimeout = 2,connectTimeoutUnit = TimeUnit.SECONDS, 
                        readTimeout = 1, readTimeoutUnit = TimeUnit.SECONDS, followRedirects = false, 
                        maxAutoRetriesNextServer = 1, maxAutoRetries = 1, isAllowedRetry = true)
                List<User> getUsers(String name);
            
                @PostMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
                @RpcInfo(connectTimeout = 5,connectTimeoutUnit = TimeUnit.SECONDS, readTimeout = 2, readTimeoutUnit = TimeUnit.SECONDS)
                String handleFileUpload(@RequestPart(value = "file") MultipartFile file);
            }

服務消費者引入 maven 依賴:

        <dependency>
             <groupId>org.example</groupId>
             <artifactId>openfeign-strengthen-plugin-start</artifactId>
             <version>1.0-SNAPSHOT</version>
        </dependency>

服務消費者方使用 application.yml 配置的形式使用,並且服務消費者超時時間配置優先級高於服務提供方配置

  feign:
    client:
      rpcConfig:
        #單個方法維度的配置
        #接口名稱 + # + 方法名稱 + (參數類型, 參數類型...)
        #包名 + 接口名稱 + # + 方法名稱 + (參數類型, 參數類型...) com.huihuang.service.MemberService#getUsers(String)
        MemberService#getUsers(String):
          connectTimeout: 1 #連接超時
          connectTimeoutUnit: SECONDS #超時時間單位
          readTimeout: 1 #讀取超時
          readTimeoutUnit: SECONDS
          followRedirects: false #是否允許重定向
          maxAutoRetries: 2 # 對當前實例的重試次數
          maxAutoRetriesNextServer: 1 # 切換實例的重試次數
        #按接口維度配置 優先級低於 單個方法維度的配置
        #接口名稱
        #包名 + 接口名稱 com.huihuang.service.MemberService
        MemberService:
          connectTimeout: 1 #連接超時
          connectTimeoutUnit: SECONDS #超時時間單位
          readTimeout: 1 #讀取超時
          readTimeoutUnit: SECONDS
          followRedirects: false #是否允許重定向
          maxAutoRetries: 2 # 對當前實例的重試次數
          maxAutoRetriesNextServer: 1 # 切換實例的重試次數
        #全局維度配置 優先級低於 單個方法維度 和 接口維度 的配置
        default:
          connectTimeout: 1 #連接超時
          connectTimeoutUnit: SECONDS #超時時間單位
          readTimeout: 1 #讀取超時
          readTimeoutUnit: SECONDS
          followRedirects: false #是否允許重定向
          maxAutoRetries: 2 # 對當前實例的重試次數
          maxAutoRetriesNextServer: 1 # 切換實例的重試次數

GitHub地址://github.com/RaidenXin/openfeign-strengthen-plugin

這是我自己遇到的問題,希望能給各位看官大大一點點幫助。如果有什麼意見和建議歡迎和我聯繫,可以直接評論或者在我的微信公眾號後台留言發給我,筆者現在在公司的職位是基礎架構架構師,如果有其他的Java方面或者基礎架構方面的問題也可以一起交流一下。非常感謝大家的觀看。

下面是我的公眾號: