why
當我們使用一個新技術的時候,應該首先問的一個問題就是why:為什么要使用這個技術,?或者問:這個技術是可以解決什么問題,。 我也想寫篇微服務的文章,以及微服務的優(yōu)缺點 在微服務架構中,,當一個大型系統(tǒng)被拆分成微服務系統(tǒng)以后,,不僅包括功能拆分,還包括系統(tǒng)拆分,、代碼拆分,、數據庫拆分、緩存拆分等,,多個系統(tǒng)的部署,、維護、調用關系,、調度,、監(jiān)控、fail over就會成為一系列問題,。同時微服務系統(tǒng)劃分越多,,調用鏈路可能會越長,調用鏈監(jiān)控,、全鏈路trace也會成為問題,。 自然和自然的法則在黑夜中隱藏,上帝說讓牛頓誕生吧,,于是一切都被照亮,。spring cloud 就是這樣誕生的。spring cloud為服務治理而生,。
舉個栗子,,當一個大型系統(tǒng)被拆分成5個小業(yè)務系統(tǒng)以后,最容易想到的后端架構是:
這樣問題很明顯,,client需要維持5個業(yè)務系統(tǒng)地址,,可能經常出現某個動作需要調用超過1個業(yè)務系統(tǒng)才能完成,而且無法保證事務性,。于是出現了下面一個架構:
spring cloud簡介
先來看一下spring cloud包含了什么組件:
spring cloud config
- 遠程配置服務。
- 遠程配置是每個都必不可少的中間件,,遠程配置的特點一般需要:多節(jié)點主備、配置化,、動態(tài)修改,、配置本地化緩存,、動態(tài)修改的實時推送等,。
- config允許配置文件放在git上或者svn上,,和spring boot的集成非常容易,,但是缺點就是修改了git上的配置以后,,只能一個一個的請求每個service的接口,,讓他們去更新配置,,沒有修改配置的推送消息,。而且,,如果要根據配置文件的修改,,做一些重新初始化操作的話(如線程池的容量變化等),,會需要一些work around的方法,,所以建議如果有其他方案,,不建議選擇spring cloud config。
spring cloud bus
- 事件,、消息總線,,用于在集群(例如,配置變化事件)中傳播狀態(tài)變化,。經常與Spring Cloud Config聯合使用,。
- spring cloud config本身不能向注冊過來的服務提供實時更新的推送。比如我們配置放在了git上,,那么當修改github上配置內容的時候,,最多可以配置webhook到一臺config-server上,但是config-server自己不會將配置更新實時推送到各個服務上。
- bus的作用就是將大家鏈接在一條總線上,,這條線上的所有server共享狀態(tài),,當webhook到bus上的某一臺server的時候,,其他server也會收到相同的hook狀態(tài),。
- 但是bus的使用需要依賴于MQ,,bus直接繼承了RabbitMq & kafka,,只需要在spring中直接配置地址即可,但是對于其他類型的MQ,,就需要一些手動配置,。
- 最大的問題還是,如果僅僅因為spring cloud bus而讓自己的系統(tǒng)引入MQ,,顯然會有些得不償失。我理解系統(tǒng)應該在滿足現有業(yè)務需求的基礎上,,越簡單越好,,依賴越少鏈路越短,越能減少出問題的風險,。
eureka
- spring cloud的服務發(fā)現組件,。這個組件講起來需要大篇幅,最好和consul一起講,。
- eureka負責服務注冊和服務發(fā)現,,為了高可用,一般需要多個eureka server相互注冊,,組成集群,。Eureka Server的同步遵循著一個非常簡單的原則:只要有一條邊將節(jié)點連接,就可以進行信息傳播與同步,。
- eureka內部對于注冊的service主要通過心跳來監(jiān)控service是否已經掛掉,,默認心跳時間是15s。這就意味著,,當一個服務提供方掛掉以后,,服務訂閱者最長可能30s以后才發(fā)現。
- service啟動連上eureka之后,,會同步一份服務列表到本地緩存,,服務注冊有更新時,,eureka會推送到每個service。
- eureka也會有一些策略防止由于某個服務所在網絡的不穩(wěn)定導致的所有服務心跳停止的雪崩現象,。
- eureka自帶web頁面,,在頁面上能看到所有的服務注冊情況 和 eureka集群狀態(tài)。
- eureka支持服務自己主動下掉自己,,請求service的下列地址,,可以讓服務從eureka上下掉自己,同時service進程也會自己停掉自己,。
curl -H 'Accept:application/json' -X POST localhost:${management.port}/shutdown
consul
- 也是一個服務發(fā)現工具,,而且自帶key-value存儲服務、健康檢查 和 web頁面,。
- 聽起來好像比eureka高大上一些,,里面使用了gossip協(xié)議和Raft協(xié)議,但是他的缺點就是比eureka難維護,。
- 服務注冊是微服務架構的關鍵節(jié)點,。所以我們現階段選擇的是eureka,然后遠程配置使用的是spring cloud config,。如果要上容器和編排的話,,會再看具體情況做選擇。
- 但是,,后來發(fā)現其實consul提供了官方的docker鏡像,,直接使用docker-consul集群用戶服務發(fā)現的話,運維成本會直線下降,,后面會考慮把eureka + spring cloud config 換成consul,。
ribbon:
- 客戶端負載均衡組件。
- 服務發(fā)現以后,,每個service在本地知道自己要調用的服務有多少臺機器,,機器的ip是什么,端口號是多少,,那這個service在本地需要有一個負載均衡策略,,為每一次請求選擇一臺目標機器進行調用,而ribbon做的就是負載均衡策略的選擇,。
- ribbon提供了多種負載均衡策略,,包括BestAvailableRule、AvailabilityFilteringRule,、WeightedResponseTimeRule,、RetryRule、RoundRobinRule,、RandomRule,、ZoneAvoidanceRule等,,沒記錯的話,默認是ZoneAvoidanceRule,。當然,,也可以自定義自己的負載均衡策略,比如被調用服務需要灰度發(fā)布或者A/B測試的話,,就可以在ribbon這一層做自定義,。
feign
- 聲明式、模板化的HTTP客戶端,。
- 微服務之間的調用本質還是http請求,,如果對于每個請求都需要寫請求代碼,增加請求參數,,同時對請求結果做處理,就會存在大量重復工作,,而feign非常優(yōu)雅的幫助我們解決了這個問題,,只需要定義一個interface,fegin就知道http請求的時候參數應該如何設置,。
- 同時,,feign也集成了ribbon,只要在微服務中依賴了ribbon,,feign默認會使用ribbon定義的負載均衡策略,。
- 最重要的是,feign并不是僅僅只能使用在有eureka或者ribbon的微服務系統(tǒng)中,,任何系統(tǒng)中,,只要涉及到http調用第三方服務,都可以使用feign,,幫我們解決http請求的代碼重復編寫,。
hystrix
- 斷路器,類似于物理電路圖中的斷路器,。
- 正常情況下,,當整個服務環(huán)境中,某一個服務提供方由于網絡原因,、數據庫原因或者性能原因等,,造成響應很慢的話,調用方就有可能短時間內累計大量的請求線程,,最終造成調用方down,,甚至整個系統(tǒng)崩潰。而加入hystrix之后,,如果hystrix發(fā)現某個服務的某臺機器調用非常緩慢或者多次調用失敗,,就會短時間內把這條路斷掉,,所有的請求都不會再發(fā)到這臺機器上。
- 如果某個服務所有的機器都掛了,,hystrix會迅速失敗,,馬上返回,保證被調用方不會有大量的線程堆積,。
- Feign默認集成了hystrix,。
- 上面有提到,使用eureka時,,當一個服務提供方掛掉以后,,服務訂閱者最長可能30s以后才知道,那這30s就會出現大量的調用失敗,。如果在系統(tǒng)里面集成了hystrix,,就會馬上把掛掉的這臺服務提供方斷路掉,讓請求不再轉發(fā)到這臺機器上,,大量減少調用失敗,。
- hystrix執(zhí)行斷路操作以后,并不表示這條路就永遠斷了,,而是會一定時間間隔內緩慢嘗試去請求這條路,,如果能請求成功,斷路就會恢復,。
- 有一點需要注意的是hystrix在做斷路時,,默認所有的調用請求都會放在一個的線程池中進行,線程池的作用很明顯,,有隔離性,。比如gateway,集成了5個子業(yè)務系統(tǒng),,可能其中一個系統(tǒng)的調用量非常大,,而另外四個系統(tǒng)的調用很小,如果沒有線程池的話,,顯然第一個系統(tǒng)的大量調用會影響到后面四個系統(tǒng)的調用性能,。hystrix的線程池和java標準線程池一樣,可以配置一些參數:coreSize,、maximumSize,、maxQueueSize、queueSizeRejectionThreshold,、allowMaximumSizeToDivergeFromCoreSize,、keepAliveTimeMinutes等,如果某一個子系統(tǒng)的調用量突然激增,,超過了線程池的容量,,也會迅速失敗,,直接返回,起到降級和保護系統(tǒng)本身的作用,。當然hystrix也支持非線程池的方式,,在本地請求線程中做調用,即semaphore模式,,官方不建議,,除非系統(tǒng)qps真的很大。
zuul
- 是一個網關組件,。提供動態(tài)路由,監(jiān)控,彈性,安全等邊緣服務的框架,。
- zuul主需要簡單配置一下properties文件,不需要寫具體的代碼就可以實現將請求轉發(fā)到相應的服務上去,。
- 還可以定制化一些filter做驗證,、隔離、限流,、文件處理等切面,,對于網關來說,使用zuul能減少大量的代碼,。
- 不過我沒有使用過,不太了解,,現在我們的網關主要還是基于feignClient,、ribbon、hystrix來實現的,。zuul默認也集成了這些組件,。有興趣可以研究研究。
turbine
- 是聚合服務器發(fā)送事件流數據的一個工具,,用來監(jiān)控集群下hystrix的metrics情況.
- 在復雜的分布式系統(tǒng)中,,相同服務的節(jié)點經常需要部署上百甚至上千個,很多時候,,運維人員希望能夠把相同服務的節(jié)點狀態(tài)以一個整體集群的形式展現出來,,這樣可以更好的把握整個系統(tǒng)的狀態(tài)。
- turbine提供把多個hystrix.stream的內容聚合為一個數據源供Dashboard展示.
Spring Cloud Starters
- spring boot熱插拔,、提供默認配置,、開箱即用的依賴。
- starter 是spring boot框架非?;A的部分,。可以自定義starter,。
我們現階段的后臺系統(tǒng)中,,上述除了spring cloud bus,、consul和zuul,其他都使用到,。Talking is cheap, github地址: https://github.com/chxfantasy/spring-cloud-demo