擼了一個 Feign 增強包 前言 問題 使用 實現 總結

前言

最近準備將公司的一個核心業務系統用 Java 進行重構,大半年沒寫 JavaJDK 都更新到 14 了,考慮到穩定性等問題最終還是選擇的 JDK11

在整體架構選型時,由於是一個全新的系統,所以沒有歷史包袱,同時團隊中也有多位大牛坐鎮,因此我們的選項便大膽起來。

最終結果就是直接一把梭,直接上未來的大趨勢:Service Mesh,直接把什麼 SpringCloudDubbo 這類分佈式框架全部幹掉。

本次的重點不是討論 Service Mesh 是什麼、能解決什麼問題、爲什麼選擇它,畢竟我也在學習階段,啥時候整明白線上也穩定了再和大家來交流。

問題

既然方向定了就開始實際擼碼了,不過剛一開始就驗證了”理想很豐滿、現實很骨感“;

由於我們去掉了 SpringCloudDubbo 這類框架,服務的註冊、發現、負載均衡等需求全部都下沉到 Service Mesh 中提供了。

但對於開發來說依然希望可以調用本地方法的方式來調用遠程服務,這在 SpringCloud 這類框架中是很容易實現的,框架本身就有很好的支持。

回到我們這個場景,需求其實很簡單,就是想達到 SpringCloud 中的 Feign 這樣的聲明式+註解的方式調用。

    @Autowired
    private StoreClient client ;
    
    Store store = client.update(1, store)

使用 spring-cloud-openfeign 這個包其實就能實現上述的需求了,但這樣會引入一些我們根本不會使用的 SpringCloud 的相關依賴,讓人感覺”不乾淨了“;同時也和 Service Mesh 的理念相反,其中的一大目的就是要降低這類框架的侵入性。


其實 spring-cloud-openfeign 的核心就是 Feign,本身它也是可以開箱即用的,所以便嘗試看 Feign 自己是否支持這樣的用法。

通過官方文檔可以得知:是可以定義接口的形式來調用遠程接口的,但它本質上是不依賴其他庫便可以使用,所以它本身是沒有和 Spring 整合也是合情合理,但也就造成了沒有現成庫可供我們使用。

我們自然是不想寫上圖紅框處的代碼的,希望所有接口直接注入就可以使用。

使用

因此結合以上的需求便有了這個庫 feign-plus

它的使用流程其實就是翻版的 spring-cloud-openfeign

@FeignPlusClient(name = "github", url = "${github.url}")
public interface Github {

    @RequestLine("GET /repos/{owner}/{repo}/contributors")
    List<GitHubRes> contributors(@Param("owner") String owner, @Param("repo") String repo);
}

SpringBoot 入口進行掃描:

@SpringBootApplication
@EnableFeignPlusClients(basePackages = "top.crossoverjie.feign.test")
public class DemoApplication {

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

}

Spring 上下文中直接注入使用:

    @Autowired
    private Github github ;
    
    List<GitHubRes> contributors = github.contributors("crossoverJie", "feign-plus");
    logger.info("contributors={}", new Gson().toJson(contributors));    

所以當我們需要調用一些外部第三方接口時(比如支付寶、外部 OpenAPI)便可類似於這樣定義一個接口,把所有 HTTP 請求的細節屏蔽掉。

當然也適合公司內部之間的服務調用,和咱們以前寫 SpringCloudDubbo 時類似;服務提供方提供一個 Client 包,消費方直接依賴便可以調用。其他的負載均衡、容錯之類的由 Service Mesh 替我們完成。

對於內部接口,也可以加上 @RequestMapping("/path") 註解:

在請求時便會在 url 後拼接上 /order,這樣在配置 feign.order.service.url 時只需要填入服務提供方的域名或 IP 即可。


feign-plus 也支持切換具體的 httpclient,默認是 okhttp3,通過以下配置便可更改。

# default(okhttp3)
feign.httpclient=http2Client

當然也有其他相關配置:

feign.plus.max-idle-connections = 520
feign.plus.connect-timeout = 11000
feign.plus.read-timeout = 12000

實現

最後簡單聊聊是如何完成的吧,其實本質上就是 spring-cloud-openfeign 的濃縮版。

其中最爲核心的便是 top.crossoverjie.feign.plus.factory.FeignPlusBeanFactory 類。

該類實現了 org.springframework.beans.factory.FactoryBean接口,並重寫了 getObject() 方法返回一個對象。

這段代碼是不是似曾相識,其實就是 Feign 的官方 demo

這裏所返回的對象其實就是我們定義的接口的代理對象,而這個對象本身則是 Feign ,所以再往裏說:我們的 http 請求編解碼、發起請求等邏輯又被這個 feign 對象所代理了。

這個 HardCodedTarget 則是 Feign 內部用於代理最終請求的對象。

有一個小難受的地方:這樣的自己定義 Bean 然後注入對象 Idea 是識別不了的,認爲當前上下文沒有該 Bean,但是 spring-cloud-openfeign 卻可以識別。


由於 Feign 支持多個客戶端,所以這裏的客戶端可以通過配置文件動態指定。

利用 SpringBoot 提供的 @ConditionalOnExpression 註解可以根據配置動態的選擇使用哪個 httpclient,也就是動態選擇生成哪個 Bean

總結

這個庫的邏輯非常簡單,本質上就是封裝了 Feign 並提供了 SpringBoot 的支持,歡迎有類似需求的朋友下載使用。

feign-plus源碼:https://github.com/crossoverJie/feign-plus

你的點贊與分享是對我最大的支持

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