Spring Cloud(Finchley版)-09-Feign

http://www.globalshopping.top

經過前文講解,我們已使用Eureka實現服務發現;使用Ribbon實現了負載均衡這種聽起來很高端的東西。我們的架構已經初具雛形,但依然存在很多問題,下面不妨來分析下前文的代碼——

1
2
3
4
5
6
7
8
9
10
11
@GetMapping("/users/{id}")
public User findById(@PathVariable Long id) {
  // 這裏用到了RestTemplate的佔位符能力
  User user = this.restTemplate.getForObject(
    "http://microservice-provider-user/users/{id}",
    User.class,
    id
  );
  // ...電影微服務的業務...
  return user;
}

這裏,this.restTemplate.getForObject("http://microservice-provider-user/users/{id}"…這行代碼是比較糟糕的,存在諸多問題——

  • 如果系統業務非常複雜,而你是一個新人,當你看到這行代碼,恐怕很難一眼看出其用途是什麼!此時,你很可能需要尋求老同事的幫助(往往是這行代碼的作者,哈哈哈,可萬一離職了呢?),或者查閱該目標地址對應的文檔(文檔常常還和代碼不匹配,哈哈哈),才能清晰瞭解這行代碼背後的含義!否則,你只能陷入蛋疼的境地!
  • 這個例子構造的URL非常簡單,但如果你需要構造類似如下這麼醜陋的URL時(原諒我老是拿百度開涮,其實我沒有惡意):https://www.baidu.com/s?wd=asf&rsv_spt=1&rsv_iqid=0xa25bbeba000047fd&issp=1&f=8&rsv_bp=0&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_sug3=3&rsv_sug1=2&rsv_sug7=100&rsv_sug2=0&inputT=328&rsv_sug4=328,恐怕就有心無力了!儘管RestTemplate支持使用佔位符,從而讓我們避免字符串拼接的尷尬境地,但構造這麼複雜的URL依然是很麻煩的。更可怕的是,互聯網時代需求變化非常之快,你的參數可能會從10個變成12個、15個,再後來又精簡成13個……維護這個URL真的是想想都累……總而言之,複雜URL的構造會讓你處於一種不性福的狀態

鋪墊了這麼多,如何解決以上問題?用Feign!

TIPS

  • Feign本質上來說是個山寨,其設計思想基本都來源於Retrofit(使用方式更是如出一轍)
  • Retrofit的GitHub:https://github.com/square/retrofit ,如果你知道Square公司,那麼你很厲害!是的,Retrofit也是開源OKHttp的那家公司開源的——所以,筆者喜歡將Square公司稱爲‘’HTTP客戶端小王子”,但其實人家是做移動支付的。
  • Spring Cloud對Retrofit也有支持https://github.com/spring-cloud-incubator/spring-cloud-square ,目前正在孵化中,有興趣的可以去體驗一下。學會Feign後,Retrofit上手也就是5分鐘的事情。

簡介

Feign是Netflix開發的聲明式、模板化的HTTP客戶端,其靈感來自Retrofit、JAXRS-2.0以及WebSocket。Feign可幫助我們更加便捷、優雅地調用HTTP API。

在Spring Cloud中,使用Feign非常簡單——只需創建接口,並在接口上添加註解即可。

Feign支持多種註解,例如Feign自帶的註解或者JAX-RS註解等。Spring Cloud對Feign進行了增強,使其支持Spring MVC註解,另外還整合了Ribbon和Eureka,從而使得Feign的使用更加方便。

TIPS

Feign的GitHub:https://github.com/OpenFeign/feign

Quick Start

下面來將前面的例子用Feign改寫,讓其達到與Ribbon + RestTemplate相同的效果。

  • 複製項目microservice-consumer-movie,將ArtifactId修改爲microservice-consumer-movie-feign ;

  • 加依賴:

    1
    2
    3
    4
    
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  • 加註解:啓動類上添加@EnableFeignClients ;

  • 編寫Feign Client:

    1
    2
    3
    4
    5
    
    @FeignClient(name = "microservice-provider-user")
    public interface UserFeignClient {
      @GetMapping("/users/{id}")
      User findById(@PathVariable("id") Long id);
    }
    

    這樣一個Feign Client就寫完啦!其中,@FeignClient 註解中的microservice-provider-user是想要請求服務的名稱,這是用來創建Ribbon Client的(Feign整合了Ribbon)。在本例中,由於使用了Eureka,所以Ribbon會把microservice-provider-user 解析成Eureka Server中的服務。

    除此之外,還可使用url屬性指定請求的URL(URL可以是完整的URL或主機名),例如@FeignClient(name = "abcde", url = "http://localhost:8000/") 。此時,name可以是任意值,但不可省略,否則應用將無法啓動!

  • Controller:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    @RequestMapping("/movies")
    @RestController
    public class MovieController {
      @Autowired
      private UserFeignClient userFeignClient;
    
      @GetMapping("/users/{id}")
      public User findById(@PathVariable Long id) {
        return this.userFeignClient.findById(id);
      }
    }
    

    只需使用@Autowire註解,即可注入上面編寫的Feign Client。

RestTemplate與Feign對比

相信通過本文的例子,聰明的你對如何使用Feign已經瞭然於心了。文章的最後,對比一下RestTemplate + Ribbon與Feign。

角度 RestTemplate + Ribbon Feign(自帶Ribbon)
可讀性、可維護性 欠佳(無法從URL直觀瞭解這個遠程調用是幹什麼的) 極佳(能在接口上寫註釋,方法名稱也是可讀的,能一眼看出這個遠程調用是幹什麼的)
開發體驗 欠佳(拼湊URL不性福 極佳(寫出漂亮的代碼,女朋友更愛你了
風格一致性 欠佳(本地API調用和RestTemplate調用的代碼風格截然不同) 極佳(完全一致,不點開Feign的接口,根本不會察覺這是一個遠程調用而非本地API調用)
性能 較好 中等(性能是RestTemplate的50%左右;如果爲Feign配置連接池,性能可提升15%左右)
靈活性 極佳 中等(內置功能能滿足大多數項目的需求)

那麼如何選擇呢?相信這纔是大家最關注的問題!

筆者認爲——

  • 一般來說,建議使用Feign,並杜絕使用RestTmplate。爲什麼用Feign相信不必囉嗦;可爲什麼要杜絕RestTemplate,那是因爲在一個項目裏,保持統一的編碼風格乃至體驗,是非常重要的。我個人的架構原則是儘量減少開發人員的選擇,如果A能解決問題,就杜絕使用B——最佳實踐永遠只有一個!並且,共存帶來的往往不是相得益彰,反而是歧義、錯亂以及額外的學習成本、理解成本(筆者當年參與過一個同時使用Struts1 + Struts2 + Servlet的項目,可以想象一下學習成本有多高;筆者還參與一個一個使用Jackson + FastJson + json-lib + Gson的項目,可想而知操作JSON的代碼有多混亂……80%的開發在罵娘中度過時光,並抨擊別人使用他不熟悉的JSON操作庫,後來被筆者統一成Jackson後,大家都安心幹活了)!
  • 有人可能會對Feign的性能存在顧慮,筆者認爲,Feign的性能雖然不那麼優秀,但大部分場景下都是OK的——項目的性能瓶頸一般都不出在HTTP客戶端上,而在於自身業務的處理!
  • 求同存異——上文雖說要杜絕RestTemplate,但事無絕對,你得根據具體情況具體分析——對於某些變態需求,在使用Feign很難實現或無法實現時,可考慮使用RestTemplate + Feign共存的方式……Spring Cloud官方也承認,無論Fegin怎麼改進,其靈活性也無法比得上RestTemplate!但是,這麼做之前請務必慎重,記住,共存帶來的往往不是相得益彰,反而是歧義、錯亂以及額外的學習成本、理解成本。

配套代碼

GitHub:https://github.com/eacdy/spring-cloud-study/tree/master/2018-Finchley/microservice-consumer-movie-feign

Gitee:https://gitee.com/itmuch/spring-cloud-study/tree/master/2018-Finchley/microservice-consumer-movie-feign

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