http://www.globalshopping.top
經過前文講解,我們已使用Eureka實現服務發現;使用Ribbon實現了負載均衡這種聽起來很高端的東西。我們的架構已經初具雛形,但依然存在很多問題,下面不妨來分析下前文的代碼——
|
|
這裏,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!但是,這麼做之前請務必慎重,記住,共存帶來的往往不是相得益彰,反而是歧義、錯亂以及額外的學習成本、理解成本。