微服務與Spring Cloud

1. 服務註冊和發現Eureka

包含3種角色:html

  • Register Service:服務註冊中心,它是一個 Eureka Server,提供服務註冊和發現的功能。git

  • Provider Service:服務提供者,它是一個 Eureka Client,提供服務。github

  • Consumer Service:服務消費者,它是 一個 Eureka Cient,消費服務。面試

1.1 服務消費的基本過程

首先須要一個服務註冊中心 Eureka Server,服務提供者 Eureka Client 向服務註冊中心 Eureka Server 註冊,將本身的信息(好比服務名和服務的 IP地址等) 經過REST API的形式提交給服務註冊中心 Eureka Server。一樣,服務消費者 Eureka Client也向服務註冊中心Eureka Server 註冊,同時服務消費者獲取一份服務註冊列表的信息 ,該列表包含了全部向服務註冊中心Eureka Server註冊的服務信息 獲取服務註冊列表信息以後 ,服務消費者就知道服務提供者的 IP地址,能夠經過http遠程調用來消費服務提供者的服務。緩存

1.2 主要概念

服務註冊(register)安全

eureka client向server註冊時,提供自身的元數據,好比ip地址、端口等性能優化

服務續約(renew)服務器

eureka client在默認狀況下會每隔30秒發送一次心跳來進行服務續約。經過服務續約來告知server該client仍然可用,沒有出現故障。正常時間若是server在90秒內沒有收到client的心跳,server會將client實例從註冊列表中刪除。網絡

獲取服務註冊列表信息(fetch registries)架構

client從server獲取服務註冊表信息,將其緩存在本地,並根據服務註冊列表信息查找其餘服務的信息,從而進行遠程調用。

服務下線(cancel)

client在程序關閉時能夠向server發送下線請求。

服務剔除(eviction)

默認狀況下,client連續90秒沒有向server發送服務續約(即心跳)時,server會將服務從服務註冊列表刪除,即服務剔除。

1.3 高可用架構

eureka高可用架構以下圖所示:

                

1.4 Eureka的高性能設計

掘金-【雙11狂歡的背後】微服務註冊中心如何承載大型系統的千萬級訪問?

2. 負載均衡Ribbon

負載均衡是指將負載分攤到多個執行單元上,常見的負載均衡有兩種方式。一種是獨立進程單元,經過負載均衡策略,將請求轉發到不一樣的執行單元上,例如 Ngnix。另外一種是將負載均衡邏輯以代碼的形式封裝到服務消費者的客戶端上服務消費者客戶端維護了一份服務提供者的信息列表 ,有了信息列表,經過負載均衡策略將請求分攤給多個服務提供者,從而達到負載均衡的目的,如Ribbon。

在Spring Cloud構建的微服務系統中, Ribbon做爲服務消費者的負載均衡器,有兩種使用方式,一種是和RestTemplate相結合,另外一種是和Feign相結合。

負載均衡器的核心類爲 LoadBalancerClient, LoadBalancerClient能夠獲取負載均衡的服務提供者的實例信息。LoadBalancerClient的choose(」eureka-client'’)方法能夠輪流獲得 eureka-client 的多個服務實例的信息。那麼負載均衡器是怎麼獲取到這些客戶端的信息的呢?查看官方文檔能夠知道 ,負載均衡器 LoadBalancerClient是從 Eureka Client獲取服務註冊列表信息的,並將服務註冊列表信息緩存了一份。 在 LoadBalancerClient 調用 choose()方法時,根據負載均衡策略選擇一個服務實例的信息,從而進行了負載均衡。 LoadBalancerClient也能夠不從 Eureka Client獲取註冊列表信息, 這時須要本身維護一份服務註冊列表信息。若是禁止Ribbon從Eureka獲取註冊列表信息(配置項ribbon.eureka.enabled=false),則須要本身去維護一份服務註冊列表信息, 根據本身維護服務註冊列表的信息, Ribbon也能夠實現負載均衡。

Ribbon的負載均衡主要是經過LoadBalancerClient來實現的,而 LoadBalancer­Client具體交給了ILoadBalancer來處理, ILoadBalancer經過配置IRule、IPing等,向EurekaClient獲取註冊列表的信息,默認每10秒向EurekaClient發送一次「ping」, 進而檢查是否須要更新服務的註冊列表信息。最後, 在獲得服務註冊列表信息後,ILoadBalancer根據IRule 的策略進行負載均衡。

3. 聲明式調用Feign

Feign是一個僞Java Http客戶端,Feign不作任何的請求處理。Feign經過處理註解生成Request模板,從而簡化了Http API的開發。開發人員可使用註解的方式定製Request API模板。在發送Http Request請求以前 ,Feign經過處理註解的方式替換掉Request模板中的參數, 生成真正的Request,並交給 Java Http客戶端去處理 。 利用這種方式,開發者只須要關注 Feign註解模板的開發 ,而不用關注Http請求自己,簡化了Http請求的過程 ,使得Http請求變得簡單和容易理解。

在Feign中,Client是一個很是重要的組件,Feign最終發送Request請求以及接收Response響應都是由Client組件完成的。Client在Feign源碼中是一個接口,在默認的狀況下,Client的實現類是Client.Default, Client.Default是由HttpURLConnnection來實現網絡請求的。另外,Client還支持 HttpClientOkHttp來進行網絡請求。

Feign如何實現負載均衡?最終負載均衡交給loadBalancerContext來處理,即Ribbon。

Feign的源碼實現過程以下

(1)首先經過@EnableFeignClients註解開啓FeignClient的功能。只有這個註解存在,纔會在程序啓動時開啓對@FeignClient註解的包掃描。
(2)根據Feign的規則實現接口,並在接口上面加上@FeignClient註解。
(3)程序啓動後,會進行包掃描,掃描全部的@FeignClient的註解的類,並將這些信息注入IoC容器中。
(4)當接口的方法被調用時,經過JDK的代理來生成具體的RequestTemplate模板對象。 
(5)根據RequestTemplate再生成Http請求的Request對象。
(6)Request對象交給Client去處理,其中Client的網絡請求框架能夠是HttpURLConnection、HttpClient和OkHttp。
(7)最後Client被封裝到LoadBalanceClient類,這個類結合類Ribbon作到了負載均衡。

4. 路由網關SpringCloud Zuul

爲何須要Zuul

(1)Zuul、Ribbon以及Eureka相結合,能夠實現智能路由和負載均衡的功能,Zuul可以將請求流量按某種策略分發到集羣狀態的多個服務實例。
(2)網關將全部服務的API接口統一聚合,並統一對外暴露。外界系統調用API接口時,都是由網關對外暴露的API接口,外界系統不須要知道微服務系統中各服務相互調用的複雜性。微服務系統也保護了其內部微服務單元的API接口,防止其被外界直接調用,致使服務的敏感信息對外暴露。

(3)網關服務能夠作用戶身份認證和權限認證,防止非法請求操做API接口,對服務器起到保護做用。
(4)網關能夠實現監控功能,實時日誌輸出,對請求進行記錄。網關能夠用來實現流量監控,在高流量的狀況下,對服務進行降級。
(5)API接口從內部服務分離出來,方便作測試。

Zuul的工做原理

Zuul是經過Servlet 來實現的,Zuul經過自定義的ZuulServlet (相似於 Spring MVC的DispatcServlet〕來對請求進行控制。Zuul的核心是一系列過濾器,能夠在Http請求的發起和響應返回期間執行一系列的過濾器。Zuul包括如下4種過濾器。

  • PRE過濾器:它是在請求路由到具體的服務以前執行的,這種類型的過濾器能夠作安全驗證 ,例如身份驗證、參數驗證等。

  • ROUTING過濾器:它用於將請求路由到具體的微服務實例。在默認狀況下, 它使用Http Client進行網絡請求。

  • POST 過濾器:它是在請求己被路由到微服務後執行的。通常狀況下,用做收集統計信息、指標,以及將響應傳輸到客戶端 。

  • ERROR 過濾器:它是在其餘過濾器發生錯誤時執行的。

Zuul請求的生命週期以下圖所示:

                        

若是服務存在多個實例,Zuul結合Ribbon會作負載均衡,將請求分發路由到不一樣的服務實例。若是不須要用Ribbon作負載均衡,能夠指定服務實例的url,直接訪問指定的url(在實際開發中不可取),若是指定url,而且想作負載均衡,那麼就須要本身維護負載均衡的服務註冊列表,首先將ribbon.eureka.enabled設置爲false,即Ribbon負載均衡客戶端不向Eureka Client獲取服務註冊列表信息,而後須要本身維護一份註冊列表。

在Zuul上配置熔斷器,能夠與Ribbon、Eureka和Hystirx等組件相結合,實現負載均衡、熔斷器的功能。

Zuul的常見使用方式,Zuul是採用了相似於Spring MVC 的 DispatchServlet來實現的,採用的是異步阻塞模型,因此性能比Ngnix差。因爲Zuul和其餘 Netflix組件能夠相互配合、無縫集成 , Zuul很容易就能實現負載均衡、智能路由和熔斷器等功能。在大多數狀況下,Zuul都是以集羣的形式存在的。因爲 Zuul的橫向擴展能力很是好,因此當負載太高時,能夠經過添加實例來解決性能瓶頸。

一種常見的使用方式是對不一樣的渠道使用不一樣的Zuul來進行路由,例如移動端共用一個Zuul網關實例 ,Web端用另外一個Zuul網關實例,其餘的客戶端用另一個Zuul實例進行路由。這種不一樣的渠邊用不一樣 Zuul實例的架構以下圖所示。

                           

另一種常見的集羣是經過Ngnix和Zuul相互結合來作負載均衡。暴露在最外面的是Ngnix主從雙熱備進行Keepalive, Ngnix通過某種路由策略,將請求路由轉發到Zuul集羣上, Zuul最終將請求分發到具體的服務上。架構圖以下圖所示。

                                

5. 總體分析

掘金-拜託!面試請不要再問我Spring Cloud底層原理

掘金-【性能優化之道】每秒上萬併發下的Spring Cloud參數優化實戰

參考資料

掘金-大白話聊聊Java併發面試問題之微服務註冊中心的讀寫鎖優化【石杉的架構筆記】

芋道源碼-註冊中心 Eureka源碼解析——應用實例註冊發現(四)之自我保護機制

程序猿DD-微服務架構的基礎框架選擇:Spring Cloud仍是Dubbo?

cnblogs-兩大微服務框架Dubbo和SpringCloud的對比

開源中國-服務註冊中心,Eureka與Zookeeper比較

Eureka確保AP,當網絡分割故障發生時,只要有一臺Eureka還在就能保證註冊服務可用(保證可用性),只不過查到的信息可能不是最新的(不保證強一致性)。內置心跳服務、自我保護模式,支持客戶端緩存,設計哲學是同時保留」好數據「與」壞數據「總比丟掉任何」好數據「要更好,高可用與可伸縮的服務發現服務。

Zookeeper保證CP,因網絡問題使得ZK集羣失去master節點是較大機率會發生的事,選舉耗時較長且期間整個ZK集羣都是不可用的,任什麼時候刻對ZK的訪問請求能獲得一致的數據結果,做爲分佈式協調服務其職責就是保證數據(注:配置數據,狀態數據)在其管轄下的全部服務之間保持同步、一致。做爲一個分佈式協同服務,ZooKeeper很是好,可是對於Service發現服務來講就不合適。

InfoQ-詳解Eureka緩存機制

因爲Eureka自己存在較多緩存,服務狀態更新滯後,最多見的情況是:服務下線後狀態沒有及時更新,服務消費者調用到已下線的服務致使請求失敗。

從CAP理論看,Eureka是一個AP系統,優先保證可用性(A)和分區容錯性(P),不保證強一致性(C),只保證最終一致性,所以在架構中設計了較多緩存

相關文章
相關標籤/搜索