本文假設讀者已經熟悉分布式系統的常見模式,如服務發現、注冊,統一配置管理等
前言
系統一旦走向分布式,其複雜程度成倍增長,傳統單體應用隻考慮業務邏輯的開發方式已經不再适用。正因其複雜性,目前隻有業務需求大的大型網際網路公司才會(被迫)采用,而且需要投入大量的技術力量來開發基礎設施,也造成了小公司“用不起”分布式架構的情況。現在這一局面正在逐漸被打破,因為Netflix開源了其經過實戰考驗的一系列基礎設施構件,加上Spring Cloud的大力支援,開發分布式系統已經不再像以前那樣可怕了。
統一的配置管理
微服務意味着要将單體應用中的業務拆分成一個個子服務,每個服務的粒度相對較小,是以系統中會出現大量的服務。由于每個服務都需要必要的配置資訊才能運作,是以一套集中式的、動态的配置管理設施是必不可少的。Spring Cloud提供了Config Server來解決這個問題。Config Server的主要功能如下:
-
集中化的配置檔案管理
不再需要在每個服務部署的機器上編寫配置檔案,服務會向配置中心統一拉取配置自己的資訊。
-
動态化的配置更新
當配置發生變動時,服務不需要重新開機即可感覺到配置的變化并應用新的配置。
-
将配置資訊以HTTP REST接口的形式暴露
這使得非Java語言的服務也能享受到上面兩點功能。
Config Server的架構圖如下:

工作流程如下:
- 配置中心感覺到配置變化(如,git倉庫發生commit送出),向Bus投遞消息
- Bus向服務廣播消息
- 服務收到消息後,主動向配置中心拉取新配置并應用
其中配置中心可叢集部署實作高可用。
服務的注冊發現和LB
Spring Cloud Netflix通過Eureka Server實作服務注冊中心,通過Ribbon實作軟負載均衡:
Zone
Eureka支援Region和Zone的概念。其中一個Region可以包含多個Zone。Eureka在啟動時需要指定一個Zone名,即目前Eureka屬于哪個zone, 如果不指定則屬于defaultZone。Eureka Client也需要指定Zone, Client(當與Ribbon配置使用時)在向Server擷取注冊清單時會優先向自己Zone的Eureka發請求,如果自己Zone中的Eureka全挂了才會嘗試向其它Zone。當擷取到遠端服務清單後,Client也會優先向同一個Zone的服務發起遠端調用。Region和Zone可以對應于現實中的大區和機房,如在華北地區有10個機房,在華南地區有20個機房,那麼分别為Eureka指定合理的Region和Zone能有效避免跨機房調用,同時一個地區的Eureka壞掉不會導緻整個該地區的服務都不可用。
Ribbon軟負載均衡
Ribbon工作在服務的調用方,分成兩步,第一步先選擇 Eureka Server, 它優先選擇在同一個Zone且負載較少的server, 第二步從Eureka中擷取到目标服務全部可用的位址,再根據使用者指定的政策,從清單中選擇一個位址作為最終要發請求的目标伺服器。其中Ribbon提供了三種政策:輪詢、斷路器和根據響應時間權重。
聲明式HTTP REST用戶端Feign
Feign與Apache Http Client這類用戶端最大的不同,是它允許你通過定義接口的形式構造HTTP請求,不需要手動拼參數,使用起來與正常的本地調用沒有什麼差別:
@FeignClient(name = "ea")
public interface AdvertGroupRemoteService {
@RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET)
AdvertGroupVO findByGroupId(@PathVariable("groupId") Integer adGroupId);
}
這裡我們隻需要調用
AdvertGroupRemoteService.findByGroupId()
方法就能完成向目标主機發HTTP請求并封裝傳回結果的效果。
斷路器、資源隔離與自我修複
斷路器(Cricuit Breaker)是一種能夠在遠端服務不可用時自動熔斷(打開開關),并在遠端服務恢複時自動恢複(閉合開關)的設施,Spring Cloud通過Netflix的Hystrix元件提供斷路器、資源隔離與自我修複功能。下面介紹一下在分布式系統中為什麼需要斷路器。
在微服務架構中,對于用戶端的一個請求,我們可能需要調用多個子(微)服務,如圖所示:
上圖中的請求需要調用A, P, H, I 四個服務,如果一切順利則沒有什麼問題,關鍵是如果I服務逾時會出現什麼情況呢?
當服務I由于某種原因無法響應時,使用者請求就會卡在服務 I 的遠端調用上,假如逾時失敗時間設定為2秒,那麼在這2秒内容器的目前線程就會一直被堵塞在該調用中。 我們假設容器最大線程數為100, 如果此時又有另外99個請求都需要調用服務 I, 那麼這99個線程同樣會被堵塞,這樣就會因為容器線程耗盡而導緻該應用無法響應其它任何請求。因為一個服務挂掉而導緻整個應用不可用顯然是無法接受的。那麼 Hystrix 是如何解決這個問題的呢?
資源隔離
首選,Hystrix對每一個依賴服務都配置了一個線程池,對依賴服務的調用會線上程池中執行。例如,我們設計服務 I 的線程池大小為20, 那麼 Hystrix會最多允許有20個容器線程調用服務 I, 如果超出20,Hystrix會拒絕并快速失敗。這樣即使服務 I 長時間未響應,容器最多也隻能堵塞20個線程,剩餘80個線程仍然可以處理使用者請求。
快速失敗
快速失敗是防止資源耗盡的關鍵一點。當 Hystrix 發現在過去某段時間内對服務 I 的調用出錯率達到某個閥值時,Hystrix 就會“熔斷”該服務,後續任何向服務 I 的請求都會快速失敗,而不是白白讓調用線程去等待。
自我修複
處于熔斷狀态的服務,在經過一段時間後,Hystrix會讓其進入“半關閉”狀态,即允許少量請求通過,然後統計調用的成功率。如果這個請求都能成功,Hystrix 會恢複該服務,進而達到自我修複的效果。其中,在服務被熔斷到進入半關閉狀态之間的時間,就是留給開發人員排查錯誤并恢複故障的時間,開發人員可以通過監控措施得到提醒并線上排查。
監控方案
監控是保障分布式系統健康運作必不可少的方案。基于Spring Cloud,我們可以從兩個緯度進行監控:Hystrix斷路器的監控和每個服務監控狀況的監控。
下圖是 Hystrix 提供的 Dashboard 圖形化監控:
可見圖中監控資訊應有盡有(調用成功率、平時響應時間、調用頻次、斷路器狀态等)。我們可以通過程式設計的方式定時擷取該資訊,并在斷路器熔斷時通過短信、郵件等方式通知開發者。
Hystrix的監控資料預設是儲存在每個執行個體的記憶體中的,Spring Boot提供了多種方式,可以導入到Redis、TSDB以供日後分析使用。
除此之外,Spring Cloud還提供了對單個執行個體的監控:
其中包含了接口調用頻次,響應時間,JVM狀态,動态日志等各種開發者關心的資訊。
總結
以上是Spring Cloud Netflix為微服務架構提供的支援,Spring Cloud項目還有許多其它子項目有着更強大的功能,通過這些元件,我們能以 Spring Boot 簡潔的風格快速搭建微服務架構,并讓開發人員專注于業務,讓分布式對開發者盡量透明。
歡迎通路Spring Cloud論壇交流經驗:http://bbs.spring-cloud.io/