假設你有一個服務A,要調用服務B(有三個實例,B一、B二、B3),如何只調用其中的B1和B2,屏蔽掉B3?實際上解決方法大體分爲兩類。java
一種是外部路由,就是經過網關等組件,在請求鏈路上進行路由選擇,即A -> 網關 -> B。python
另外一種是內部路由,即A服務藉助一些第三方庫,直接決定要訪問的B服務實例,即A -> B。spring
本文重點介紹內部路由的一種實現方式~markdown
一句話來講,Spring Cloud Ribbon是Netflix Ribbon實現的客戶端負載均衡器。一張圖能解釋它的做用,從一個服務的多個實例中,最終選擇一個實例用於消費。併發
對於沒有使用過Spring Cloud的同窗來講可能有點陌生,若是你寫過Spring Boot應用,要調用的遠程服務僅經過IP加端口的形式暴露,遠程調用的姿式多是這樣的。app
RestTemplate restTemplate = new RestTemplate();
String url = "http://10.10.10.10:8080";
String result = restTemplate.getForObject(url, String.class);
複製代碼
當被消費的服務作高可用,有多個實例的時候,這樣的調用方式徹底不可行。使用Ribbon的話,就能夠將一個服務名映射到多個實例上。大體的使用方式以下,首先在配置文件中加上本身想要的服務名,以及該服務名後面映射的實例地址,在調用的時,url直接使用服務名。負載均衡
# application.properties
# 服務名 netease-cloud
netease-cloud.ribbon.listOfServers=10.10.10.10:8080,10.10.10.20:8080,10.10.10.30:8080
# code snippet
String url = "http://netease-cloud";
String result = restTemplate.getForObject(url, String.class);
複製代碼
Ribbon的原理也很簡單,就是對RestTemplate進行了攔截,將服務名轉成了實例列表,而後根據對應的負載均衡規則選擇其中一個實例進行真正的調用。因此這個例子裏的RestTemplate並不是是普通的RestTemplate,是通過Ribbon增強的版本。框架
事實上Ribbon每每配合註冊中心一塊兒使用,由註冊中心週期性地提供實例列表供它使用。做爲一個負載均衡器,它已經能知足大部分的需求了,基於它的實現原理事實上能夠進行增強,從而知足一些更高級的需求。好比,運維
上面兩點其實是路由的功能,它能夠經過實現Ribbon的一些過濾器(ServerListFilter)來作,可是沒法在不重啓應用的狀況下,動態地改變路由規則。微服務
若是能強化Ribbon,加上路由功能和動態規則配置功能,能實現不少的玩法。
在加強Ribbon以前,有必要拆開這個黑盒,瞭解一下它裏面的比較重要的組件。
整體的數據流向就是上圖所示。
這裏有個細節影響了加強的總體方案——ServerList和ServerListFilter默認每30秒更新一次實例列表到Cached Server List(本身取的名)中。若是本身實現了ServerListFilter或ServerList,即便經過手段進行了實時的替換,也不能馬上提供最新的實例列表供IRule使用。對於實時性要求高的應用,是沒法容忍的。
至此,對於加強的方案來講,必然要選擇代價小的辦法。最好的方式是可以經過Ribbon提供的拓展來實現,而不是直接改動源碼。
通過實踐,能夠經過自定義的IRule,實現了動態路由功能。思路是,使用默認的ServerList和ServerListFilter,將全部的邏輯都在自定義的IRule之中實現。官方對IRule的定義是:A strategy for loadbalancing,一種負載均衡策略。咱們賦予它更多的職能,官方實現的IRule(好比:輪詢、隨機)僅僅作了選擇的工做,咱們自定義的IRule包含了過濾和選擇的功能。
整個自定義IRule的邏輯能夠參照上圖,在Custom Rule獲得實例列表後,先通過一系列的filter,好比黑白名單、區域優先路由、參數分流,過濾掉不知足要求的實例,再將過濾後的實例列表傳遞給Custom Selector,它作了IRule真正要作的工做,根據規則選定最終的實例。
那麼如何動態的配置Custom Filters和Custom Selector?簡單來講,由於IRule是固定的(即咱們自定義的),只要持有該對象的引用,就能隨時更改其中的Filter和Selector。那麼該應用如何收到配置變動的指令呢?最簡單的方式,讓該應用暴露一個接口,使用者調用該接口。不過該方式太不優雅,勝在工做量小,但不適合在生產環境使用。
以上方案若是要本身去實現,仍是須要一些工做量,有沒有一種無侵入式、代碼零改造、操做方便的方案呢?此時須要祭出咱們組裏的祕密武器——NSF微服務框架。NSF微服務框架主要包含NSF Server(控制面)和NSF Agent(數據面)。NSF Agent的原理是經過javaagent方式加強用戶應用的代碼,在java類被初始化加載以前對目標類進行加強。用戶應用以agent方式啓動後,agent會在用戶應用側開放端口用於和NSF Server進行通訊(grpc協議),使用者在NSF Server側進行規則配置後,規則會被封裝而後下發至agent,agent會負責將處理規則,使之生效。像前面說的路由規則就能夠在NSF Server側進行配置下發,而後由agent生效。其實整個方案還要複雜的多,可是核心大體是這樣,細節的話本文就不贅述了。
若是僅僅爲了一個動態路由和動態負載均衡的能力,就作這樣一套東西未免有殺雞用牛刀的感受。NSF固然不僅支持這兩個功能,熔斷降級、服務發現、服務容錯、流量染色、服務監控、動態配置等等功能,都在NSF的能力範圍內。
這樣一套微服務框架看起來很是美好,可是事實上也確實如此。只要你的應用基於spring boot或者使用dubbo,NSF均可以平滑接入。這裏的平滑接入是指不改動應用的任何一行代碼,就讓應用具有各類強大的服務治理、流量控制、監控等功能。若是你還不知足於此,須要一套能幫助大家快速實現易接入、易運維的微服務解決方案,歡迎試用咱們部門的輕舟(已商用)。
不使用java的同窗也不要失望,咱們組裏也有一套基於istio和envoy的service mesh方案,無論你是go、python仍是其餘任何語言,均可以無侵入地進行微服務化改造。
來源:網易工程師-陳佳翰
任何有疑惑的地方,歡迎你們找我討論,謝謝。
看到這裏的小夥伴,若是你喜歡這篇文章的話,別忘了轉發、收藏、留言互動!
若是對文章有任何問題,歡迎在留言區和我交流~
最近我新整理了一些Java資料,包含面經分享、模擬試題、和視頻乾貨,若是你須要的話,歡迎留言or私信我!