說明
在遠古時代算盤可以解決算力問題。在20世紀初期電腦單機還是少數精英的特權,在21世紀互聯網風起雲湧, 高配置單機服務器不僅貴也應對不了海嘯般的流量。集羣、分佈式系統來解圍,SpringCloud微服務作爲解決方案的佼佼者出現了。(解決方案類似於:三個臭皮匠勝過一個諸葛亮,如果不行就先來一億個臭皮匠。)
微服務(Microservices Architecture)是一種架構風格,一個大型複雜軟件應用由一個或多個微服務組成。系統中的各個微服務可被獨立部署,各個微服務之間是松耦合的。每個微服務僅關注於完成一件任務並很好地完成該任務。在所有情況下,每個任務代表着一個小的業務能力。
微服務的概念源於2014年3月Martin Fowler所寫的章“Microservices”http://martinfowler.com/articles/microservices.html
Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分佈式系統的開發,比如服務發現、服務網關、服務路由、鏈路追蹤等。Spring Cloud 並不重複造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從而減少了各模塊的開發成本。換句話說:Spring Cloud 提供了構建分佈式系統所需的“全家桶”。
接下來先簡單說明SpringCloud的相關概念,集羣、分佈式系統、微服務。
1. 什麼是集羣
計算機集羣簡稱集羣是一種計算機系統,它通過一組鬆散集成的計算機軟件和/或硬件連接起來高度緊密地協作完成計算工作。在某種意義上,他們可以被看作是一臺計算機。集羣系統中的單個計算機通常稱爲節點,通常通過局域網連接,但也有其它的可能連接方式。集羣計算機通常用來改進單個計算機的計算速度和/或可靠性。一般情況下集羣計算機比單個計算機,比如工作站或超級計算機性能價格比要高得多. (維基百科)
集羣技術特點:
- 通過多臺計算機完成同一個工作,達到更高的效率。
- 兩機或多機內容、工作過程等完全一樣。如果一臺死機,另一臺可以照常工作。
- 同一個業務,部署在多個服務器上(不同的服務器運行同樣的代碼,幹同一件事)。
2. 什麼是分佈式
分佈式系統是一組計算機,通過網絡相互連接傳遞消息與通信後並協調它們的行爲而形成的系統。組件之間彼此進行交互以實現一個共同的目標。(維基百科)
將每個模塊抽取獨立出來,訪問量大的模塊用好的服務器裝着,沒啥人訪問的模塊用差的服務器裝着。這樣的好處是:一、資源合理利用了(沒人訪問的模塊用性能差的服務器,訪問量大的模塊單獨提升性能就好了)。二、耦合度降低了:每個模塊獨立出來,各幹各的事(專業的人做專業的事),便於擴展
分佈式:一個業務分拆多個子業務,部署在不同的服務器上(不同的服務器,運行不同的代碼,爲了同一個目的)
特點:
- 將大功能拆分,模塊之間獨立,在使用的時候再將這些獨立的模塊組合起來就是一個系統了。
好處:
- 模塊之間獨立,各做各的事,便於擴展,複用性高
- 高吞吐量。某個任務需要一個機器運行10個小時,將該任務用10臺機器的分佈式跑(將這個任務拆分成10個小任務),可能2個小時就跑完了
3. CAP理論
從上面所講的分佈式概念我們已經知道,分佈式簡單理解就是:一個業務分拆多個子業務,部署在不同的服務器上
一般來說,一個子業務我們稱爲節點。
如果你接觸過一些分佈式的基礎概念,那肯定會聽過CAP這個理論。就比如說:你學了MySQL的InnoDB存儲引擎相關知識,你肯定聽過ACID!
首先,我們來看一下CAP分別代表的是什麼意思:
- C:數據一致性(consistency),所有節點擁有數據的最新版本
- A:可用性(availability),數據具備高可用性
- P:分區容錯性(partition-tolerance),容忍網絡出現分區,分區之間網絡不可達。
下面有三個節點(它們是集羣的),此時三個節點都能夠相互通信:
由於我們的系統是分佈式的,節點之間的通信是通過網絡來進行的。只要是分佈式系統,那很有可能會出現一種情況:因爲一些故障,使得有些節點之間不連通了,整個網絡就分成了幾塊區域。
數據就散佈在了這些不連通的區域中,這就叫分區
現在出現了網絡分區後,此時有一個請求過來了,想要註冊一個賬戶。
此時我們節點一和節點三是不可通信的,這就有了抉擇:
- 如果允許當前用戶註冊一個賬戶,此時註冊的記錄數據只會在節點一和節點二或者節點二和節點三同步,因爲節點一和節點三的記錄不能同步的。
- 這種情況其實就是選擇了可用性(availability),拋棄了數據一致性(consistency)
- 如果不允許當前用戶註冊一個賬戶(就是要等到節點一和節點三恢復通信)。節點一和節點三一旦恢復通信,我們就可以保證節點擁有的數據是最新版本。
- 這種情況其實就是拋棄了可用性(availability),選擇了數據一致性(consistency)
4. CAP理論中間狀態只能三者選其二
一般我們說的分佈式系統,P:分區容錯性(partition-tolerance)這個是必需的,這是客觀存在的。
CAP是無法完全兼顧的,從上面的例子也可以看出,我們可以選AP,也可以選CP。但是,要注意的是:不是說選了AP,C就完全拋棄了。不是說選了CP,A就完全拋棄了!
在CAP理論中,C所表示的一致性是強一致性(每個節點的數據都是最新版本),其實一致性還有其他級別的:
- 弱一致性:弱一致性是相對於強一致性而言,它不保證總能得到最新的值;
- 最終一致性(eventual consistency):放寬對時間的要求,在被調完成操作響應後的某個時間點,被調多個節點的數據最終達成一致
可用性的值域可以定義成0到100%的連續區間。
可用性分類 | 可用水平(%) | 年容忍停機時間 |
---|---|---|
容錯可用性 | 99.9999(6個9) | < 1 min |
極高可用性 | 99.999(5個9) | < 5 min |
具有故障自動恢復能力的可用性 | 99.99(4個9) | < 53 min |
高可用性 | 99.9(3個9) | < 8.8 h |
商品可用性 | 99(2個9) | < 43.8 h |
所以,CAP理論定義的其實是在容忍網絡分區的條件下,“強一致性”和“極致可用性”無法同時達到。
5. 爲什麼需要SpringCloud
前面也講了,從分佈式/微服務的角度而言:就是把我們一大的項目,分解成多個小的模塊。這些小的模塊組合起來,完成功能。
舉個可能不太恰當的例子(現實可能不會這麼拆分,但意思到位就好了):
拆分出多個模塊以後,就會出現各種各樣的問題,而SpringCloud提供了一整套的解決方案!
- 注:這些模塊是獨立成一個子系統的(不同主機)。
SpringCloud的基礎功能:
- 服務治理: Spring Cloud Eureka
- 客戶端負載均衡: Spring Cloud Ribbon
- 服務容錯保護: Spring Cloud Hystrix
- 聲明式服務調用: Spring Cloud Feign
- API網關服務:Spring Cloud Zuul
- 分佈式配置中心: Spring Cloud Config
SpringCloud的高級功能:
- 消息總線: Spring Cloud Bus
- 消息驅動的微服務: Spring Cloud Stream
- 分佈式服務跟蹤: Spring Cloud Sleuth
Spring Cloud 優缺點
其主要優點有:
- 集大成者,Spring Cloud 包含了微服務架構的方方面面。
- 約定優於配置,基於註解,沒有配置文件。
- 輕量級組件,Spring Cloud 整合的組件大多比較輕量級,且都是各自領域的佼佼者。
- 開發簡便,Spring Cloud 對各個組件進行了大量的封裝,從而簡化了開發。
- 開發靈活,Spring Cloud 的組件都是解耦的,開發人員可以靈活按需選擇組件。
接下來,我們看下它的缺點:
- 項目結構複雜,每一個組件或者每一個服務都需要創建一個項目。
- 部署門檻高,項目部署需要配合 Docker 等容器技術進行集羣部署,而要想深入瞭解 Docker,學習成本高。
Spring Cloud 的優勢是顯而易見的。因此對於想研究微服務架構的同學來說,學習 Spring Cloud 是一個不錯的選擇。
Spring Cloud 和 Dubbo 對比
Dubbo 只是實現了服務治理,而 Spring Cloud 實現了微服務架構的方方面面,服務治理只是其中的一個方面。下面通過一張圖對其進行比較:
核心要素 | Dubbo | Spring Cloud |
---|---|---|
服務註冊中心 | Zookepper, Redis | Spring Cloud Netflix Eureka |
服務調用方式 | RPC | REST API |
服務網關 | 無 | Spring Cloud Netflix Zuul |
熔斷隔離 | 不完善 | Spring Cloud Netflix Hystrix |
分佈式配置 | 無 | Spring Cloud Config |
分佈式追蹤系統 | 無 | Spring Cloud Sleuth |
消息總線 | 無 | Spring Cloud Bus |
數據流 | 無 | Spring Cloud Stream 基於Redis, Rabbit, Kafka實現的消息微服務 |
批量任務 | 無 | Spring Cloud Task |
可以看出,Spring Cloud 比較全面,而 Dubbo 由於只實現了服務治理,需要集成其他模塊,需要單獨引入,增加了學習成本和集成成本。
SpringCloud的版本關係
SpringCloud是一個由許多子項目組成的綜合項目,各子項目有不同的發佈節奏。爲了管理SpringCloud與各子項目的版本依賴關係,發佈了一個清單,其中包括了某個SpringCloud版本對應的子項目版本。
爲了避免SpringCloud版本號與子項目版本號混淆,SpringCloud版本採用了名稱而非版本號的命名,這些版本的名字採用了倫敦地鐵站的名字,根據字母表的順序來對應版本時間順序,例如Angel是第一個版本, Brixton是第二個版本。
當SpringCloud的發佈內容積累到臨界點或者一個重大BUG被解決後,會發佈一個"service releases"版本,簡稱SRX版本,比如Greenwich.SR2就是SpringCloud發佈的Greenwich版本的第2個SRX版本。
SpringCloud和SpringBoot版本對應關係
SpringCloud Version | SpringBoot Version |
---|---|
Hoxton | 2.2.x |
Greenwich | 2.1.x |
Finchley | 2.0.x |
Edgware | 1.5.x |
Dalston | 1.5.x |
SpringCloud和各子項目版本對應關係
Component | Edgware.SR6 | Greenwich.SR2 |
---|---|---|
spring-cloud-netflix | 1.4.7.RELEASE | 2.1.2.RELEASE |
spring-cloud-consul | 1.3.6.RELEASE | 2.1.2.RELEASE |
spring-cloud-gateway | 1.0.3.RELEASE | 2.1.2.RELEASE |
spring-cloud-openfeign | Null | 2.1.2.RELEASE |
spring-cloud-config | 1.4.7.RELEASE | 2.1.3.RELEASE |
spring-cloud-security | 1.2.4.RELEASE | 2.1.2.RELEASE |
spring-cloud-bus | 1.3.4.RELEASE | 2.1.2.RELEASE |
spring-cloud-commons | 1.3.6.RELEASE | 2.1.2.RELEASE |
spring-cloud-sleuth | 1.3.6.RELEASE | 2.1.1.RELEASE |
spring-cloud-stream | Ditmars.SR5 | Fishtown.SR3 |
spring-cloud-zookeeper | 1.2.3.RELEASE | 2.1.2.RELEASE |
spring-cloud-task | 1.2.4.RELEASE | 2.1.2.RELEASE |
注意:Greenwich版本是基於SpringBoot 2.1.x版本構建的,不適用於1.5.x版本。隨着2019年8月SpringBoot 1.5.x版本停止維護,Edgware版本也將停止維護。
6. 引出Eureka
那會出現什麼問題呢??首當其衝的就是子系統之間的通訊問題。子系統與子系統之間不是在同一個環境下,那就需要遠程調用。遠程調用可能就會想到httpClient,WebService等等這些技術來實現。
既然是遠程調用,就必須知道ip地址,我們可能有以下的場景。
- 功能實現一:A服務需要調用B服務
** 在A服務的代碼裏面調用B服務,顯式通過IP地址調用:http://123.123.123.123:8888/zgpeace
- 功能實現二:A服務調用B服務,B服務調用C服務,C服務調用D服務
** 在A服務的代碼裏面調用B服務,顯式通過IP地址調用:http://123.123.123.123:8888/zgpeace
,(同樣地)B->C,C->D - 功能實現三:D服務調用B服務,B服務調用C服務
** 在D服務的代碼裏面調用B服務,顯式通過IP地址調用:http://123.123.123.123:8888/zgpeace
,(同樣地)B->C - …等等等等
萬一,我們B服務的IP地址變了,想想會出現什麼問題:A服務,D服務(等等)需要手動更新B服務的地址
- 在服務多的情況下,手動來維護這些靜態配置就是噩夢!
爲了解決微服務架構中的服務實例維護問題(ip地址), 產生了大量的服務治理框架和產品。 這些框架和產品的實現都圍繞着服務註冊與服務發現機制來完成對微服務應用實例的自動化管理。
在SpringCloud中我們的服務治理框架一般使用的就是Eureka。
我們的問題:
- 現在有A、B、C、D四個服務,它們之間會互相調用(而且IP地址很可能會發生變化),一旦某個服務的IP地址變了,那服務中的代碼要跟着變,手動維護這些靜態配置(IP)非常麻煩!
Eureka是這樣解決上面所說的情況的:
- 創建一個E服務,將A、B、C、D四個服務的信息都註冊到E服務上,E服務維護這些已經註冊進來的信息
A、B、C、D四個服務都可以拿到Eureka(服務E)那份註冊清單。A、B、C、D四個服務互相調用不再通過具體的IP地址,而是通過服務名來調用! - 拿到註冊清單—>註冊清單上有服務名—>自然就能夠拿到服務具體的位置了(IP)。
- 其實簡單來說就是:代碼中通過服務名找到對應的IP地址(IP地址會變,但服務名一般不會變)
7. Eureka細節
Eureka專門用於給其他服務註冊的稱爲Eureka Server(服務註冊中心),其餘註冊到Eureka Server的服務稱爲Eureka Client
在Eureka Server一般我們會這樣配置:
register-with-eureka: false #false表示不向註冊中心註冊自己。
fetch-registry: false #false表示自己端就是註冊中心,我的職責就是維護服務實例,並不需要去檢索服務
Eureka Client分爲服務提供者和服務消費者。
- 但很可能,某服務既是服務提供者又是服務消費者。
如果在網上看到SpringCloud的某個服務配置沒有"註冊"到Eureka-Server也不用過於驚訝(但是它是可以獲取Eureka服務清單的)
- 很可能只是作者把該服務認作爲單純的服務消費者,單純的服務消費者無需對外提供服務,也就無須註冊到Eureka中了
eureka:
client:
register-with-eureka: false # 當前微服務不註冊到eureka中(消費端)
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
下面是Eureka的治理機制:
- 服務提供者
- 服務註冊:啓動的時候會通過發送REST請求的方式將自己註冊到Eureka Server上,同時帶上了自身服務的一些元數據信息。
- 服務續約:在註冊完服務之後,服務提供者會維護一個心跳用來持續告訴Eureka Server: "我還活着 ” 、
- 服務下線:當服務實例進行正常的關閉操作時,它會觸發一個服務下線的REST請求給Eureka Server, 告訴服務註冊中心:“我要下線了 ”。
- 服務消費者
- 獲取服務:當我們啓動服務消費者的時候,它會發送一個REST請求給服務註冊中心,來獲取上面註冊的服務清單
- 服務調用:服務消費者在獲取服務清單後,通過服務名可以獲得具體提供服務的實例名和該實例的元數據信息。在進行服務調用的時候,優先訪問同處一個Zone中的服務提供方。
- Eureka Server(服務註冊中心):
失效剔除:默認每隔一段時間(默認爲60秒) 將當前清單中超時(默認爲90秒)沒有續約的服務剔除出去。
自我保護:EurekaServer 在運行期間,會統計心跳失敗的比例在15分鐘之內是否低於85%(通常由於網絡不穩定導致)。 Eureka Server會將當前的實例註冊信息保護起來, 讓這些實例不會過期,儘可能保護這些註冊信息。
最後,我們就有了這張圖:
8. 引出RestTemplate和Ribbon
通過Eureka服務治理框架,我們可以通過服務名來獲取具體的服務實例的位置了(IP)。一般在使用SpringCloud的時候不需要自己手動創建HttpClient
來進行遠程調用。
可以使用Spring封裝好的RestTemplate工具類,使用起來很簡單:
// 傳統的方式,直接顯示寫死IP是不好的!
//private static final String REST_URL_PREFIX = "http://localhost:8001";
// 服務實例名
private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT";
/**
* 使用 使用restTemplate訪問restful接口非常的簡單粗暴無腦。 (url, requestMap,
* ResponseBean.class)這三個參數分別代表 REST請求地址、請求參數、HTTP響應轉換被轉換成的對象類型。
*/
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/consumer/dept/add")
public boolean add(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}
爲了實現服務的高可用,我們可以將服務提供者集羣。比如說,現在一個秒殺系統設計出來了,準備上線了。在11月11號時爲了能夠支持高併發,我們開多臺機器來支持併發量。
現在想要這三個秒殺系統合理攤分用戶的請求(專業來說就是負載均衡),可能你會想到nginx。
其實SpringCloud也支持的負載均衡功能,只不過它是客戶端的負載均衡,這個功能實現就是Ribbon!
負載均衡又區分了兩種類型:
- 客戶端負載均衡(Ribbon)
- 服務實例的清單在客戶端,客戶端進行負載均衡算法分配。
(從上面的知識我們已經知道了:客戶端可以從Eureka Server中得到一份服務清單,在發送請求時通過負載均衡算法,在多個服務器之間選擇一個進行訪問)
- 服務端負載均衡(Nginx)
- 服務實例的清單在服務端,服務器進行負載均衡算法分配
所以,我們的圖可以畫成這樣:
9. Ribbon細節
Ribbon是支持負載均衡,默認的負載均衡策略是輪詢,我們也是可以根據自己實際的需求自定義負載均衡策略的。
@Configuration
public class MySelfRule
{
@Bean
public IRule myRule()
{
//return new RandomRule();// Ribbon默認是輪詢,我自定義爲隨機
//return new RoundRobinRule();// Ribbon默認是輪詢,我自定義爲隨機
return new RandomRule_ZY();// 我自定義爲每臺機器5次
}
}
實現起來也很簡單:繼承AbstractLoadBalancerRule
類,重寫public Server choose(ILoadBalancer lb, Object key)
即可。
SpringCloud 在CAP理論是選擇了AP的,在Ribbon中還可以配置重試機制的(有興趣的同學可以去搜搜)~
10. 引出Hystrix
到目前爲止,我們的服務看起來好像挺好的了:能夠根據服務名來遠程調用其他的服務,可以實現客戶端的負載均衡。
但是,如果我們在調用多個遠程服務時,某個服務出現延遲,會怎麼樣??
在高併發的情況下,由於單個服務的延遲,可能導致所有的請求都處於延遲狀態,甚至在幾秒鐘就使服務處於負載飽和的狀態,資源耗盡,直到不可用,最終導致這個分佈式系統都不可用,這就是“雪崩”。
針對上述問題, Spring Cloud Hystrix實現了斷路器、線程隔離等一系列服務保護功能。
- Fallback(失敗快速返回):當某個服務單元發生故障(類似用電器發生短路)之後,通過斷路器的故障監控(類似熔斷保險絲), 向調用方返回一個錯誤響應, 而不是長時間的等待。這樣就不會使得線程因調用故障服務被長時間佔用不釋放,避免了故障在分佈式系統中的蔓延。
- 資源/依賴隔離(線程池隔離):它會爲每一個依賴服務創建一個獨立的線程池,這樣就算某個依賴服務出現延遲過高的情況,也只是對該依賴服務的調用產生影響, 而不會拖慢其他的依賴服務。
Hystrix提供幾個熔斷關鍵參數:滑動窗口大小(20)、 熔斷器開關間隔(5s)、錯誤率(50%)
- 每當20個請求中,有50%失敗時,熔斷器就會打開,此時再調用此服務,將會直接返回失敗,不再調遠程服務。
- 直到5s鍾之後,重新檢測該觸發條件,判斷是否把熔斷器關閉,或者繼續打開。
Hystrix還有請求合併、請求緩存這樣強大的功能.
11. Hystrix儀表盤
Hystrix儀表盤:它主要用來實時監控Hystrix的各項指標信息。通過Hystrix Dashboard反饋的實時信息,可以幫助我們快速發現系統中存在的問題,從而及時地採取應對措施。
啓動時的頁面:
監控單服務的頁面:
我們現在的服務是這樣的:
除了可以開啓單個實例的監控頁面之外,還有一個監控端點 /turbine.stream
是對集羣使用的。 從端點的命名中,可以引入Turbine, 通過它來彙集監控信息,並將聚合後的信息提供給 HystrixDashboard 來集中展示和監控。
12. 引出Feign
上面已經介紹了Ribbon和Hystrix了,可以發現的是:他倆作爲基礎工具類框架廣泛地應用在各個微服務的實現中。我們會發現對這兩個框架的使用幾乎是同時出現的。
爲了簡化我們的開發,Spring Cloud Feign出現了!它基於 Netflix Feign 實現,整合了 Spring Cloud Ribbon 與 Spring Cloud Hystrix, 除了整合這兩者的強大功能之外,它還提
供了聲明式的服務調用(不再通過RestTemplate)。
Feign是一種聲明式、模板化的HTTP客戶端。在Spring Cloud中使用Feign, 我們可以做到使用HTTP請求遠程服務時能與調用本地方法一樣的編碼體驗,開發者完全感知不到這是遠程方法,更感知不到這是個HTTP請求。
下面就簡單看看Feign是怎麼優雅地實現遠程調用的:
服務綁定:
// value --->指定調用哪個服務
// fallbackFactory--->熔斷器的降級提示
@FeignClient(value = "MICROSERVICECLOUD-DEPT", fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
// 採用Feign我們可以使用SpringMVC的註解來對服務進行綁定!
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") long id);
@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
public List<Dept> list();
@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
public boolean add(Dept dept);
}
Feign中使用熔斷器:
/**
* Feign中使用斷路器
* 這裏主要是處理異常出錯的情況(降級/熔斷時服務不可用,fallback就會找到這裏來)
*/
@Component // 不要忘記添加,不要忘記添加
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept get(long id) {
return new Dept().setDeptno(id).setDname("該ID:" + id + "沒有沒有對應的信息,Consumer客戶端提供的降級信息,此刻服務Provider已經關閉")
.setDb_source("no this database in MySQL");
}
@Override
public List<Dept> list() {
return null;
}
@Override
public boolean add(Dept dept) {
return false;
}
};
}
}
調用:
13. 引出Zuul
基於上面的學習,我們現在的架構很可能會設計成這樣:
這樣的架構會有兩個比較麻煩的問題:
- 路由規則與服務實例的維護問題:外層的負載均衡(nginx)需要維護所有的服務實例清單(圖上的OpenService)
- 簽名校驗、 登錄校驗冗餘問題:爲了保證對外服務的安全性, 我們在服務端實現的微服務接口,往往都會有一定的權限校驗機制,但我們的服務是獨立的,我們不得不在這些應用中都實現這樣一套校驗邏輯,這就會造成校驗邏輯的冗餘。
還是畫個圖來理解一下吧:
每個服務都有自己的IP地址,Nginx想要正確請求轉發到服務上,就必須維護着每個服務實例的地址!
更是災難的是:這些服務實例的IP地址還有可能會變,服務之間的劃分也很可能會變。
http://123.123.123.123
http://123.123.123.124
http://123.123.123.125
http://123.123.123.126
http://123.123.123.127
購物車和訂單模塊都需要用戶登錄了纔可以正常訪問,基於現在的架構,只能在購物車和訂單模塊都編寫校驗邏輯,這無疑是冗餘的代碼。
爲了解決上面這些常見的架構問題,API網關的概念應運而生。在SpringCloud中了提供了基於Netfl ix Zuul實現的API網關組件Spring Cloud Zuul。
Spring Cloud Zuul是這樣解決上述兩個問題的:
- SpringCloud Zuul通過與SpringCloud Eureka進行整合,將自身註冊爲Eureka服務治理下的應用,同時從Eureka中獲得了所有其他微服務的實例信息。外層調用都必須通過API網關,使得將維護服務實例的工作交給了服務治理框架自動完成。
- 在API網關服務上進行統一調用來對微服務接口做前置過濾,以實現對微服務接口的攔截和校驗。
Zuul天生就擁有線程隔離和斷路器的自我保護功能,以及對服務調用的客戶端負載均衡功能。也就是說:Zuul也是支持Hystrix和Ribbon。
關於Zuul還有很多知識點:
- 路由匹配(動態路由)
- 過濾器實現(動態過濾器)
- 默認會過濾掉Cookie與敏感的HTTP頭信息(額外配置)
14. 可能對Zuul的疑問
Zuul支持Ribbon和Hystrix,也能夠實現客戶端的負載均衡。我們的Feign不也是實現客戶端的負載均衡和Hystrix的嗎?既然Zuul已經能夠實現了,那我們的Feign還有必要嗎?
或者可以這樣理解:
- zuul是對外暴露的唯一接口相當於路由的是controller的請求,而Ribbonhe和Fegin路由了service的請求
- zuul做最外層請求的負載均衡 ,而Ribbon和Fegin做的是系統內部各個微服務的service的調用的負載均衡
有了Zuul,還需要Nginx嗎?他倆可以一起使用嗎?
- 我的理解:Zuul和Nginx是可以一起使用的(畢竟我們的Zuul也是可以搭成集羣來實現高可用的),要不要一起使用得看架構的複雜度了(業務)~~~
15. 引出SpringCloud Config
隨着業務的擴展,我們的服務會越來越多,越來越多。每個服務都有自己的配置文件。
既然是配置文件,給我們配置的東西,那難免會有些改動的。
比如我們的Demo中,每個服務都寫上相同的配置文件。萬一我們有一天,配置文件中的密碼需要更換了,那就得三個都要重新更改。
在分佈式系統中,某一個基礎服務信息變更,都很可能會引起一系列的更新和重啓
Spring Cloud Config項目是一個解決分佈式系統的配置管理方案。它包含了Client和Server兩個部分,server提供配置文件的存儲、以接口的形式將配置文件的內容提供出去,client通過接口獲取數據、並依據此數據初始化自己的應用。
- 簡單來說,使用Spring Cloud Config就是將配置文件放到統一的位置管理(比如GitHub),客戶端通過接口去獲取這些配置文件。
- 在GitHub上修改了某個配置文件,應用加載的就是修改後的配置文件。
SpringCloud Config其他的知識: - 在SpringCloud Config的服務端, 對於配置倉庫的默認實現採用了Git。
- 配置文件內的信息加密和解密
- 修改了配置文件,希望不用重啓來動態刷新配置,配合Spring Cloud Bus 使用~
使用SpringCloud Config可能的疑問:application.yml和 bootstrap.yml區別
16. 消息總線: Spring Cloud Bus
Spring Cloud Bus 將分佈式的節點用輕量的消息代理連接起來。它可以用於廣播配置文件的更改或者服務之間的通訊,也可以用於監控。本文要講述的是用Spring Cloud Bus實現通知微服務架構的配置文件的更改。
17. 消息驅動的微服務: Spring Cloud Stream
Spring Cloud Stream是創建消息驅動微服務應用的框架。Spring Cloud Stream是基於Spring Boot創建,用來建立單獨的/工業級spring應用,使用spring integration提供與消息代理之間的連接。數據流操作開發包,封裝了與Redis,Rabbit、Kafka等發送接收消息。
一個業務會牽扯到多個任務,任務之間是通過事件觸發的,這就是Spring Cloud stream要乾的事了
18. 分佈式服務跟蹤: Spring Cloud Sleuth
Spring Cloud Sleuth 主要功能就是在分佈式系統中提供追蹤解決方案,並且兼容支持了 zipkin,你只需要在pom文件中引入相應的依賴即可。
Add sleuth to the classpath of a Spring Boot application (see below for Maven and Gradle examples), and you will see the correlation data being collected in logs, as long as you are logging requests.
------ 摘自官網
服務追蹤分析
微服務架構上通過業務來劃分服務的,通過REST調用,對外暴露的一個接口,可能需要很多個服務協同才能完成這個接口功能,如果鏈路上任何一個服務出現問題或者網絡超時,都會形成導致接口調用失敗。隨着業務的不斷擴張,服務之間互相調用會越來越複雜。
隨着服務的越來越多,對調用鏈的分析會越來越複雜。它們之間的調用關係也許如下:
術語
-
Span:基本工作單元,例如,在一個新建的span中發送一個RPC等同於發送一個迴應請求給RPC,span通過一個64位ID唯一標識,trace以另一個64位ID表示,span還有其他數據信息,比如摘要、時間戳事件、關鍵值註釋(tags)、span的ID、以及進度ID(通常是IP地址)
** span在不斷的啓動和停止,同時記錄了時間信息,當你創建了一個span,你必須在未來的某個時刻停止它。 -
Trace:一系列spans組成的一個樹狀結構,例如,如果你正在跑一個分佈式大數據工程,你可能需要創建一個trace。
-
Annotation:用來及時記錄一個事件的存在,一些核心annotations用來定義一個請求的開始和結束
** cs - Client Sent -客戶端發起一個請求,這個annotion描述了這個span的開始
** sr - Server Received -服務端獲得請求並準備開始處理它,如果將其sr減去cs時間戳便可得到網絡延遲
** ss - Server Sent -註解表明請求處理的完成(當請求返回客戶端),如果ss減去sr時間戳便可得到服務端需要的處理請求時間
** cr - Client Received -表明span的結束,客戶端成功接收到服務端的回覆,如果cr減去cs時間戳便可得到客戶端從服務端獲取回覆的所有所需時間
將Span和Trace在一個系統中使用Zipkin註解的過程圖形化:
總結
本文主要寫了SpringCloud的基礎知識,希望大家看完能有所幫助~
參考
https://juejin.im/post/5b83466b6fb9a019b421cecc
https://spring.io/projects/spring-cloud#overview
https://github.com/macrozheng/springcloud-learning
https://blog.csdn.net/forezp/article/details/70148833
https://blog.csdn.net/valada/article/details/80892573
http://www.ityouknow.com/springcloud/2017/05/01/simple-springcloud.html