1 爲何須要服務發現nginx
簡單來講,服務化的核心就是將傳統的一站式應用根據業務拆分紅一個一個的服務,而微服務在這個基礎上要更完全地去耦合(再也不共享DB、KV,去掉重量級ESB),而且強調DevOps和快速演化。這就要求咱們必須採用與一站式時代、泛SOA時代不一樣的技術棧,而Spring Cloud就是其中的佼佼者。web
DevOps是英文Development和Operations的合體,他要求開發、測試、運維進行一體化的合做,進行更小、更頻繁、更自動化的應用發佈,以及圍繞應用架構來構建基礎設施的架構。這就要求應用充分的內聚,也方便運維和管理。這個理念與微服務理念不謀而合。redis
接下來咱們從服務化架構演進的角度來看看爲何Spring Cloud更適應微服務架構。緩存
1.1 從使用nginx提及服務器
最初的服務化解決方案是給提供相同服務提供一個統一的域名,而後服務調用者向這個域名發送HTTP請求,由Nginx負責請求的分發和跳轉。NginxArch.png
markdown
這種架構存在不少問題:網絡
Nginx做爲中間層,在配置文件中耦合了服務調用的邏輯,這削弱了微服務的完整性,也使得Nginx在必定程度上變成了一個重量級的ESB。架構
服務的信息分散在各個系統,沒法統一管理和維護。每一次的服務調用都是一次嘗試,服務消費者並不知道有哪些實例在給他們提供服務。這不符合DevOps的理念。app
沒法直觀的看到服務提供者和服務消費者當前的運行情況和通訊頻率。這也不符合DevOps的理念。負載均衡
消費者的失敗重發,負載均衡等都沒有統一策略,這加大了開發每一個服務的難度,不利於快速演化。
爲了解決上面的問題,咱們須要一個現成的中心組件對服務進行整合,將每一個服務的信息彙總,包括服務的組件名稱、地址、數量等。服務的調用方在請求某項服務時首先經過中心組件獲取提供這項服務的實例的信息(IP、端口等),再經過默認或自定義的策略選擇該服務的某一提供者直接進行訪問。因此,咱們引入了Dubbo。
1.2 基於Dubbo實現微服務
Dubbo是阿里開源的一個SOA服務治理解決方案,文檔豐富,在國內的使用度很是高。
使用Dubbo構建的微服務,已經能夠比較好地解決上面提到的問題:DubboArch.png
調用中間層變成了可選組件,消費者能夠直接訪問服務提供者。
服務信息被集中到Registry中,造成了服務治理的中心組件。
經過Monitor監控系統,能夠直觀地展現服務調用的統計信息。
Consumer能夠進行負載均衡、服務降級的選擇。
可是對於微服務架構而言,Dubbo也並非十全十美的:
Registry嚴重依賴第三方組件(zookeeper或者redis),當這些組件出現問題時,服務調用很快就會中斷。
DUBBO只支持RPC調用。使得服務提供方與調用方在代碼上產生了強依賴,服務提供者須要不斷將包含公共代碼的jar包打包出來供消費者使用。一旦打包出現問題,就會致使服務調用出錯。
最爲重要的是,DUBBO如今已經中止維護了,對於技術發展的新需求,須要由開發者自行拓展升級。這對於不少想要採用微服務架構的中小軟件組織,顯然是不太合適的。
目前Github社區上有一個DUBBO的升級版,叫DUBBOX,提供了更高效的RPC序列化方式和REST調用方式。可是該項目也基本中止維護了。
1.3 新的選擇——Spring Cloud
做爲新一代的服務框架,Spring Cloud提出的口號是開發「面向雲環境的應用程序」,它爲微服務架構提供了更加全面的技術支持。
結合咱們一開始提到的微服務的訴求,咱們把Spring Cloud與DUBBO進行一番對比:
Spring Cloud拋棄了Dubbo的RPC通訊,採用的是基於HTTP的REST方式。嚴格來講,這兩種方式各有優劣。雖然從必定程度上來講,後者犧牲了服務調用的性能,但也避免了上面提到的原生RPC帶來的問題。並且REST相比RPC更爲靈活,服務提供方和調用方的依賴只依靠一紙契約,不存在代碼級別的強依賴,這在強調快速演化的微服務環境下,顯得更加合適。
很明顯,Spring Cloud的功能比DUBBO更增強大,涵蓋面更廣,並且做爲Spring的拳頭項目,它也可以與Spring Framework、Spring Boot、Spring Data、Spring Batch等其餘Spring項目完美融合,這些對於微服務而言是相當重要的。前面提到,微服務背後一個重要的理念就是持續集成、快速交付,而在服務內部使用一個統一的技術框架,顯然比把分散的技術組合到一塊兒更有效率。更重要的是,相比於Dubbo,它是一個正在持續維護的、社區更加火熱的開源項目,這就保證使用它構建的系統,能夠持續地獲得開源力量的支持。
Netflix和Spring Cloud是什麼關係呢?Netflix是一家成功實踐微服務架構的互聯網公司,幾年前,Netflix就把它的幾乎整個微服務框架棧開源貢獻給了社區。Spring背後的Pivotal在2015年推出的Spring Cloud開源產品,主要對Netflix開源組件的進一步封裝,方便Spring開發人員構建微服務基礎框架。
對於微服務的治理而言,核心就是服務的註冊和發現。因此選擇哪一個組件,很大程度上要看它對於服務註冊與發現的解決方案。在這個領域,開源架構不少,最多見的是Zookeeper,但這並非一個最佳選擇。
在分佈式系統領域有個著名的CAP定理:C——數據一致性,A——服務可用性,P——服務對網絡分區故障的容錯性。這三個特性在任何分佈式系統中不能同時知足,最多同時知足兩個。
Zookeeper是著名Hadoop的一個子項目,不少場景下Zookeeper也做爲Service發現服務解決方案。Zookeeper保證的是CP,即任什麼時候刻對
Zookeeper的訪問請求能獲得一致的數據結果,同時系統對網絡分割具有容錯性,可是它不能保證每次服務請求的可用性。從實際狀況來分析,在使用Zookeeper獲取服務列表時,若是zookeeper正在選主,或者Zookeeper集羣中半數以上機器不可用,那麼將就沒法得到數據了。因此說,Zookeeper不能保證服務可用性。
誠然,對於大多數分佈式環境,尤爲是涉及到數據存儲的場景,數據一致性應該是首先被保證的,這也是zookeeper設計成CP的緣由。可是對於服務發現場景來講,狀況就不太同樣了:針對同一個服務,即便註冊中心的不一樣節點保存的服務提供者信息不盡相同,也並不會形成災難性的後果。由於對於服務消費者來講,能消費纔是最重要的——拿到可能不正確的服務實例信息後嘗試消費一下,也好過由於沒法獲取實例信息而不去消費。因此,對於服務發現而言,可用性比數據一致性更加劇要——AP賽過CP。而Spring Cloud Netflix在設計Eureka時遵照的就是AP原則。
Eureka自己是Netflix開源的一款提供服務註冊和發現的產品,而且提供了相應的Java封裝。在它的實現中,節點之間是相互平等的,部分註冊中心的節點掛掉也不會對集羣形成影響,即便集羣只剩一個節點存活,也能夠正常提供發現服務。哪怕是全部的服務註冊節點都掛了,Eureka Clients上也會緩存服務調用的信息。這就保證了咱們微服務之間的互相調用是足夠健壯的。
除此以外,Spring Cloud Netflix背後強大的開源力量,也促使咱們選擇了Spring Cloud Netflix:
前文提到過,Spring Cloud的社區十分活躍,其在業界的應用也十分普遍(尤爲是國外),並且整個框架也經受住了Netflix嚴酷生產環境的考驗。
除了服務註冊和發現,Spring Cloud Netflix的其餘功能也十分強大,包括Ribbon,hystrix,Feign,Zuul等組件,結合到一塊兒,讓服務的調用、路由也變得異常容易。
Spring Cloud Netflix做爲Spring的重量級整合框架,使用它也意味着咱們能從Spring獲取到巨大的便利。Spring Cloud的其餘子項目,好比Spring Cloud Stream、Spring Cloud Config等等,都爲微服務的各類需求提供了一站式的解決方案。
Spring Cloud Netflix的核心是用於服務註冊與發現的Eureka,接下來咱們將以Eureka爲線索,介紹Eureka、Ribbon、Hystrix、Feign這些Spring Cloud Netflix主要組件。
3.1 服務註冊與發現——Eureka
Eureka這個詞來源於古希臘語,意爲「我找到了!我發現了!」,據傳,阿基米德在洗澡時發現浮力原理,高興得來不及穿上褲子,跑到街上大喊:「Eureka(我找到了)!」。
Eureka由多個instance(服務實例)組成,這些服務實例能夠分爲兩種:Eureka Server和Eureka Client。爲了便於理解,咱們將Eureka client再分爲Service Provider和Service Consumer。以下圖所示:EurekaRole.png
Eureka Server:服務的註冊中心,負責維護註冊的服務列表。
Service Provider:服務提供方,做爲一個Eureka Client,向Eureka Server作服務註冊、續約和下線等操做,註冊的主要數據包括服務名、機器ip、端口號、域名等等。
Service Consumer:服務消費方,做爲一個Eureka Client,向Eureka Server獲取Service Provider的註冊信息,並經過遠程調用與Service Provider進行通訊。
Service Provider和Service Consumer不是嚴格的概念,Service Consumer也能夠隨時向Eureka Server註冊,來讓本身變成一個Service Provider。
Spring Cloud針對服務註冊與發現,進行了一層抽象,並提供了三種實現:Eureka、Consul、Zookeeper。目前支持得最好的就是Eureka,其次是Consul,最後是Zookeeper。
3.1.1 Eureka Server
Eureka Server做爲一個獨立的部署單元,以REST API的形式爲服務實例提供了註冊、管理和查詢等操做。同時,Eureka Server也爲咱們提供了可視化的監控頁面,能夠直觀地看到各個Eureka Server當前的運行狀態和全部已註冊服務的狀況。
3.1.1.1 Eureka Server的高可用集羣
Eureka Server能夠運行多個實例來構建集羣,解決單點問題,但不一樣於ZooKeeper的選舉leader的過程,Eureka Server採用的是Peer to Peer對等通訊。這是一種去中心化的架構,無master/slave區分,每個Peer都是對等的。在這種架構中,節點經過彼此互相註冊來提升可用性,每一個節點須要添加一個或多個有效的serviceUrl指向其餘節點。每一個節點均可被視爲其餘節點的副本。
若是某臺Eureka Server宕機,Eureka Client的請求會自動切換到新的Eureka Server節點,當宕機的服務器從新恢復後,Eureka會再次將其歸入到服務器集羣管理之中。當節點開始接受客戶端請求時,全部的操做都會進行replicateToPeer(節點間複製)操做,將請求複製到其餘Eureka Server當前所知的全部節點中。
一個新的Eureka Server節點啓動後,會首先嚐試從鄰近節點獲取全部實例註冊表信息,完成初始化。Eureka Server經過getEurekaServiceUrls()方法獲取全部的節點,而且會經過心跳續約的方式按期更新。默認配置下,若是Eureka Server在必定時間內沒有接收到某個服務實例的心跳,Eureka Server將會註銷該實例(默認爲90秒,經過eureka.instance.lease-expiration-duration-in-seconds配置)。當Eureka Server節點在短期內丟失過多的心跳時(好比發生了網絡分區故障),那麼這個節點就會進入自我保護模式。下圖爲Eureka官網的架構圖
什麼是自我保護模式?默認配置下,若是Eureka Server每分鐘收到心跳續約的數量低於一個閾值(instance的數量(60/每一個instance的心跳間隔秒數)自我保護係數),就會觸發自我保護。在自我保護模式中,Eureka Server會保護服務註冊表中的信息,再也不註銷任何服務實例。當它收到的心跳數從新恢復到閾值以上時,該Eureka Server節點就會自動退出自我保護模式。它的設計哲學前面提到過,那就是寧肯保留錯誤的服務註冊信息,也不盲目註銷任何可能健康的服務實例。該模式能夠經過eureka.server.enable-self-preservation = false來禁用,同時eureka.instance.lease-renewal-interval-in-seconds能夠用來更改心跳間隔,eureka.server.renewal-percent-threshold能夠用來修改自我保護係數(默認0.85)。
3.1.1.2 Eureka Server的Region、Zone
Eureka的官方文檔對Regin、Zone幾乎沒有說起,因爲概念抽象,新手很難理解。所以,咱們先來了解一下Region、Zone、Eureka集羣三者的關係,以下圖所示:
region和zone(或者Availability Zone)均是AWS的概念。在非AWS環境下,咱們能夠先簡單地將region理解爲Eureka集羣,zone理解成機房。上圖就能夠理解爲一個Eureka集羣被部署在了zone1機房和zone2機房中。
3.1.2 Service Provider
3.1.2.1 服務註冊
Service Provider本質上是一個Eureka Client。它啓動時,會調用服務註冊方法,向Eureka Server註冊本身的信息。Eureka Server會維護一個已註冊服務的列表,這個列表爲一個嵌套的hash map:
第一層,application name和對應的服務實例。
第二層,服務實例及其對應的註冊信息,包括IP,端口號等。
當實例狀態發生變化時(如自身檢測認爲Down的時候),也會向Eureka Server更新本身的服務狀態,同時用replicateToPeers()向其它Eureka Server節點作狀態同步。
![Uploading EurekaServerEvict_655129.png …]
3.1.2.2 續約與剔除
前面提到過,服務實例啓動後,會週期性地向Eureka Server發送心跳以續約本身的信息,避免本身的註冊信息被剔除。續約的方式與服務註冊基本一致:首先更新自身狀態,再同步到其它Peer。
若是Eureka Server在一段時間內沒有接收到某個微服務節點的心跳,Eureka Server將會註銷該微服務節點(自我保護模式除外)。
3.1.3 Service Consumer
Service Consumer本質上也是一個Eureka Client(它也會向Eureka Server註冊,只是這個註冊信息可有可無罷了)。它啓動後,會從Eureka Server上獲取全部實例的註冊信息,包括IP地址、端口等,並緩存到本地。這些信息默認每30秒更新一次。前文提到過,若是與Eureka Server通訊中斷,Service Consumer仍然能夠經過本地緩存與Service Provider通訊。
實際開發Eureka的過程當中,有時會碰見Service Consumer獲取到Server Provider的信息有延遲,在Eureka Wiki中有這麼一段話:
All operations from Eureka client may take some time to reflect in the Eureka servers and subsequently in other Eureka clients. This is because of the caching of the payload on the eureka server which is refreshed periodically to reflect new information. Eureka clients also fetch deltas periodically. Hence, it may take up to 2 mins for changes to propagate to all Eureka clients.
最後一句話提到,服務端的更改可能須要2分鐘才能傳播到全部客戶端,至於緣由並無介紹。這是由於Eureka有三處緩存和一處延遲形成的。
Eureka Server對註冊列表進行緩存,默認時間爲30s。
Eureka Client對獲取到的註冊信息進行緩存,默認時間爲30s。
Ribbon會從上面提到的Eureka Client獲取服務列表,將負載均衡後的結果緩存30s。
若是不是在Spring Cloud環境下使用這些組件(Eureka, Ribbon),服務啓動後並不會立刻向Eureka註冊,而是須要等到第一次發送心跳請求時纔會註冊。心跳請求的發送間隔默認是30s。Spring Cloud對此作了修改,服務啓動後會立刻註冊。
基於Service Consumer獲取到的服務實例信息,咱們就能夠進行服務調用了。而Spring Cloud也爲Service Consumer提供了豐富的服務調用工具:
Ribbon,實現客戶端的負載均衡。
Hystrix,斷路器。
Feign,RESTful Web Service客戶端,整合了Ribbon和Hystrix。
接下來咱們就一一介紹。
3.2 服務調用端負載均衡——Ribbon
Ribbon是Netflix發佈的開源項目,主要功能是爲REST客戶端實現負載均衡。它主要包括六個組件:
ServerList,負載均衡使用的服務器列表。這個列表會緩存在負載均衡器中,並按期更新。當Ribbon與Eureka結合使用時,ServerList的實現類就是DiscoveryEnabledNIWSServerList,它會保存Eureka Server中註冊的服務實例表。
ServerListFilter,服務器列表過濾器。這是一個接口,主要用於對Service Consumer獲取到的服務器列表進行預過濾,過濾的結果也是ServerList。Ribbon提供了多種過濾器的實現。
IPing,探測服務實例是否存活的策略。
IRule,負載均衡策略,其實現類表述的策略包括:輪詢、隨機、根據響應時間加權等。
咱們也能夠本身定義負載均衡策略,好比咱們就利用本身實現的策略,實現了服務的版本控制和直連配置。實現好以後,將實現類從新注入到Ribbon中便可。
ILoadBalancer,負載均衡器。這也是一個接口,Ribbon爲其提供了多個實現,好比ZoneAwareLoadBalancer。而上層代碼經過調用其API進行服務調用的負載均衡選擇。通常ILoadBalancer的實現類中會引用一個IRule。
RestClient,服務調用器。顧名思義,這就是負載均衡後,Ribbon向Service Provider發起REST請求的工具。
Ribbon工做時會作四件事情:
優先選擇在同一個Zone且負載較少的Eureka Server;
按期從Eureka更新並過濾服務實例列表;
根據用戶指定的策略,在從Server取到的服務註冊列表中選擇一個實例的地址;
經過RestClient進行服務調用。
3.3 服務調用端熔斷——Hystrix
Netflix建立了一個名爲Hystrix的庫,實現了斷路器的模式。「斷路器」自己是一種開關裝置,當某個服務單元發生故障以後,經過斷路器的故障監控(相似熔斷保險絲),向調用方返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者拋出調用方沒法處理的異常,這樣就保證了服務調用方的線程不會被長時間、沒必要要地佔用,從而避免了故障在分佈式系統中的蔓延,乃至雪崩。
固然,在請求失敗頻率較低的狀況下,Hystrix仍是會直接把故障返回給客戶端。只有當失敗次數達到閾值(默認在20秒內失敗5次)時,斷路器打開而且不進行後續通訊,而是直接返回備選響應。固然,Hystrix的備選響應也是能夠由開發者定製的。HystrixFallback.png
除了隔離依賴服務的調用之外,Hystrix還提供了準實時的調用監控(Hystrix Dashboard),Hystrix會持續地記錄全部經過Hystrix發起的請求的執行信息,並以統計報表和圖形的形式展現給用戶,包括每秒執行多少請求多少成功,多少失敗等。Netflix經過hystrix-metrics-event-stream項目實現了對以上指標的監控。Spring Cloud也提供了Hystrix Dashboard的整合,對監控內容轉化成可視化界面,Hystrix Dashboard Wiki上詳細說明了圖上每一個指標的含義。
3.4 服務調用端代碼抽象和封裝——Feign
Feign是一個聲明式的Web Service客戶端,它的目的就是讓Web Service調用更加簡單。它整合了Ribbon和Hystrix,從而讓咱們再也不須要顯式地使用這兩個組件。Feign還提供了HTTP請求的模板,經過編寫簡單的接口和插入註解,咱們就能夠定義好HTTP請求的參數、格式、地址等信息。接下來,Feign會徹底代理HTTP的請求,咱們只須要像調用方法同樣調用它就能夠完成服務請求。
Feign具備以下特性:
可插拔的註解支持,包括Feign註解和JAX-RS註解
支持可插拔的HTTP編碼器和解碼器
支持Hystrix和它的Fallback
支持Ribbon的負載均衡
支持HTTP請求和響應的壓縮
如下是一個Feign的簡單示例:
@SpringBootApplication
@EnableDiscoveryClient //啓用Feign
@EnableFeignClients
public class Application
{
public static void main(String[] args)
{
SpringApplication.run(Application.class, args);
}
}
@FeignClient(name = 「elements」, fallback = ElementsFallback.class) //指定feign調用的服務和Hystrix Fallback(name即eureka的application name)
public interface Elements
{
@RequestMapping(value = 「/index」)
String index();
}
//Hystrix Fallback
@Component
public class ElementsFallback implements Elements
{
@Override
public String index()
{
return 「****」;
}
}
//測試類
@Component
public class TestController {
@Autowired
Elements elements;
@RequestMapping(value = 「/testEureka」, method = RequestMethod.GET)
public String testeureka()
{
return elements.index();
}
}
文章轉自 http://www.uml.org.cn/wfw/201708032.asp?utm_source=tuicool&utm_medium=referral