目錄
- 一、微服務介紹
- 1. 什麼是微服務
- 2. 微服務由來
- 3. 為什麼需要微服務?
- 3.1 最期的單體架構帶來的問題
- 1.複雜性逐漸變高
- 2.技術債務逐漸上升
- 3.部署速度逐漸變慢
- 4.阻礙技術創新
- 5.無法按需伸縮
- 3.2 微服務與單體架構差別
- 3.3 微服務與SOA差別
- 4. 微服務本質
- 5. 什麼樣的項目适合微服務
- 6. 微服務折分與設計
- 6.1 微服務設計原則
- 單一職責原則
- 服務自治原則
- 輕量級通信原則
- 接口明确原則
- 7. 微服務優勢與缺點
- 7.1 特性
- 7.2 特點
- 易于開發和維護
- 啟動較快
- 局部修改容易部署
- 技術棧不受限
- 按需伸縮
- 7.3 缺點
- 運維要求較高
- 分布式的複雜性
- 接口調整成本高
- 重複勞動
- 8. 微服務開發架構
- 9. Sprint cloud 和 Sprint boot差別
- 二、微服務實踐先知
- 1. 用戶端如何通路這些服務?(API Gateway)
- 2. 服務之間如何通信?(服務調用)
- 3. 這麼多服務怎麼查找?(服務發現)
- 4. 服務挂了怎麼辦?
- 5. 微服務需要考慮的問題
- 三、微服務重要部件
- 1. 微服務基本能力
- 2. 服務注冊中心
- 2.1 zookeeper服務注冊和發現
- 3. 負載均衡
- 3.1 負載均衡的常見政策
- 3.1.1 随機
- 3.1.2 輪詢
- 3.1.3 權重輪詢
- 3.1.4 IP Hash
- 3.1.5 最少連接配接數
- 4. 容錯
- 4.1 容錯政策
- 4.1.1 快速失敗
- 4.1.2 失效切換
- 4.1.3 失敗安全
- 4.1.4 失敗自動恢複
- 4.1.5 forking Cluster
- 4.1.6 廣播調用
- 5. 熔斷
- 6. 限流和降級
- 7. SLA
- 8. API網關
- 9. 多級緩存
- 10. 逾時和重試
- 11. 線程池隔離
- 12. 降級和限流
- 13. 網關監控和統計
一、微服務介紹
1. 什麼是微服務
在介紹微服務時,首先得先了解什麼是微服務,顧名思義,微服務得從兩個方面去了解,什麼是"微"、什麼是"服務", 微 狹義來講就是體積小、著名的"2 pizza 團隊"很好的诠釋了這一解釋(2 pizza 團隊最早是亞馬遜 CEO Bezos提出來的,意思是說單個服務的設計,所有參與人從設計、開發、測試、運維所有人加起來 隻需要2個披薩就夠了 )。 而所謂服務,一定要差別于系統,服務一個或者一組相對較小且獨立的功能單元,是使用者可以感覺最小功能集。
2. 微服務由來
微服務最早由Martin Fowler與James Lewis于2014年共同提出,微服務架構風格是一種使用一套小服務來開發單個應用的方式途徑,每個服務運作在自己的程序中,并使用輕量級機制通信,通常是HTTP API,這些服務基于業務能力建構,并能夠通過自動化部署機制來獨立部署,這些服務使用不同的程式設計語言實作,以及不同資料存儲技術,并保持最低限度的集中式管理。
3. 為什麼需要微服務?
在傳統的IT行業軟體大多都是各種獨立系統的堆砌,這些系統的問題總結來說就是擴充性差,可靠性不高,維護成本高。到後面引入了SOA服務化,但是,由于 SOA 早期均使用了總線模式,這種總線模式是與某種技術棧強綁定的,比如:J2EE。這導緻很多企業的遺留系統很難對接,切換時間太長,成本太高,新系統穩定性的收斂也需要一些時間。最終 SOA 看起來很美,但卻成為了企業級奢侈品,中小公司都望而生畏。
3.1 最期的單體架構帶來的問題
單體架構在規模比較小的情況下工作情況良好,但是随着系統規模的擴大,它暴露出來的問題也越來越多,主要有以下幾點:
1.複雜性逐漸變高
比如有的項目有幾十萬行代碼,各個子產品之間差別比較模糊,邏輯比較混亂,代碼越多複雜性越高,越難解決遇到的問題。
2.技術債務逐漸上升
公司的人員流動是再正常不過的事情,有的員工在離職之前,疏于代碼品質的自我管束,導緻留下來很多坑,由于單體項目代碼量龐大的驚人,留下的坑很難被發覺,這就給新來的員工帶來很大的煩惱,人員流動越大所留下的坑越多,也就是所謂的技術債務越來越多。
3.部署速度逐漸變慢
這個就很好了解了,單體架構子產品非常多,代碼量非常龐大,導緻部署項目所花費的時間越來越多,曾經有的項目啟動就要一二十分鐘,這是多麼恐怖的事情啊,啟動幾次項目一天的時間就過去了,留給開發者開發的時間就非常少了。
4.阻礙技術創新
比如以前的某個項目使用struts2寫的,由于各個子產品之間有着千絲萬縷的聯系,代碼量大,邏輯不夠清楚,如果現在想用spring mvc來重構這個項目将是非常困難的,付出的成本将非常大,是以更多的時候公司不得不硬着頭皮繼續使用老的struts架構,這就阻礙了技術的創新。
5.無法按需伸縮
比如說電影子產品是CPU密集型的子產品,而訂單子產品是IO密集型的子產品,假如我們要提升訂單子產品的性能,比如加大記憶體、增加硬碟,但是由于所有的子產品都在一個架構下,是以我們在擴充訂單子產品的性能時不得不考慮其它子產品的因素,因為我們不能因為擴充某個子產品的性能而損害其它子產品的性能,進而無法按需進行伸縮。
3.2 微服務與單體架構差別
單體架構所有的子產品全都耦合在一塊,代碼量大,維護困難,微服務每個子產品就相當于一個單獨的項目,代碼量明顯減少,遇到問題也相對來說比較好解決。
單體架構所有的子產品都共用一個資料庫,存儲方式比較單一,微服務每個子產品都可以使用不同的存儲方式(比如有的用redis,有的用mysql等),資料庫也是單個子產品對應自己的資料庫。
單體架構所有的子產品開發所使用的技術一樣,微服務每個子產品都可以使用不同的開發技術,開發模式更靈活。
3.3 微服務與SOA差別
微服務,從本質意義上看,還是 SOA 架構。但内涵有所不同,微服務并不綁定某種特殊的技術,在一個微服務的系統中,可以有 Java 編寫的服務,也可以有 Python編寫的服務,他們是靠Restful架構風格統一成一個系統的。是以微服務本身與具體技術實作無關,擴充性強。
4. 微服務本質
微服務,關鍵其實不僅僅是微服務本身,而是系統要提供一套基礎的架構,這種架構使得微服務可以獨立的部署、運作、更新,不僅如此,這個系統架構還讓微服務與微服務之間在結構上“松耦合”,而在功能上則表現為一個統一的整體。這種所謂的“統一的整體”表現出來的是統一風格的界面,統一的權限管理,統一的安全政策,統一的上線過程,統一的日志和審計方法,統一的排程方式,統一的通路入口等等。
微服務的目的是有效的拆分應用,實作靈活開發和部署 。
微服務提倡的理念團隊間應該是 inter-operate, not integrate 。inter-operate是定義好系統的邊界和接口,在一個團隊内全棧,讓團隊自治,原因就是因為如果團隊按照這樣的方式組建,将溝通的成本維持在系統内部,每個子系統就會更加内聚,彼此的依賴耦合能變弱,跨系統的溝通成本也就能降低。
5. 什麼樣的項目适合微服務
微服務可以按照業務功能本身的獨立性來劃分,如果系統提供的業務是非常底層的,如:作業系統核心、存儲系統、網絡系統、資料庫系統等等,這類系統都偏底層,功能和功能之間有着緊密的配合關系,如果強制拆分為較小的服務單元,會讓內建工作量急劇上升,并且這種人為的切割無法帶來業務上的真正的隔離,是以無法做到獨立部署和運作,也就不适合做成微服務了。
能不能做成微服務,取決于四個要素:
小:微服務體積小,2 pizza 團隊。
獨:能夠獨立的部署和運作。
輕:使用輕量級的通信機制和架構。
松:為服務之間是松耦合的。
6. 微服務折分與設計
從單體式結構轉向微服務架構中會持續碰到服務邊界劃分的問題:比如,我們有user 服務來提供使用者的基礎資訊,那麼使用者的頭像和圖檔等是應該單獨劃分為一個新的service更好還是應該合并到user服務裡呢?如果服務的粒度劃分的過粗,那就回到了單體式的老路;如果過細,那服務間調用的開銷就變得不可忽視了,管理難度也會指數級增加。目前為止還沒有一個可以稱之為服務邊界劃分的标準,隻能根據不同的業務系統加以調節
拆分的大原則是當一塊業務不依賴或極少依賴其它服務,有獨立的業務語義,為超過2個的其他服務或用戶端提供資料,那麼它就應該被拆分成一個獨立的服務子產品。
6.1 微服務設計原則
單一職責原則
意思是每個微服務隻需要實作自己的業務邏輯就可以了,比如訂單管理子產品,它隻需要處理訂單的業務邏輯就可以了,其它的不必考慮。
服務自治原則
意思是每個微服務從開發、測試、運維等都是獨立的,包括存儲的資料庫也都是獨立的,自己就有一套完整的流程,我們完全可以把它當成一個項目來對待。不必依賴于其它子產品。
輕量級通信原則
首先是通信的語言非常的輕量,第二,該通信方式需要是跨語言、跨平台的,之是以要跨平台、跨語言就是為了讓每個微服務都有足夠的獨立性,可以不受技術的鉗制。
接口明确原則
由于微服務之間可能存在着調用關系,為了盡量避免以後由于某個微服務的接口變化而導緻其它微服務都做調整,在設計之初就要考慮到所有情況,讓接口盡量做的更通用,更靈活,進而盡量避免其它子產品也做調整。
7. 微服務優勢與缺點
7.1 特性
每個微服務可獨立運作在自己的程序裡;
一系列獨立運作的微服務共同建構起了整個系統;
每個服務為獨立的業務開發,一個微服務一般完成某個特定的功能,比如:訂單管理,使用者管理等;
微服務之間通過一些輕量級的通信機制進行通信,例如通過REST API或者RPC的方式進行調用。
7.2 特點
易于開發和維護
由于微服務單個子產品就相當于一個項目,開發這個子產品我們就隻需關心這個子產品的邏輯即可,代碼量和邏輯複雜度都會降低,進而易于開發和維護。
啟動較快
這是相對單個微服務來講的,相比于啟動單體架構的整個項目,啟動某個子產品的服務速度明顯是要快很多的。
局部修改容易部署
在開發中發現了一個問題,如果是單體架構的話,我們就需要重新釋出并啟動整個項目,非常耗時間,但是微服務則不同,哪個子產品出現了bug我們隻需要解決那個子產品的bug就可以了,解決完bug之後,我們隻需要重新開機這個子產品的服務即可,部署相對簡單,不必重新開機整個項目進而大大節約時間。
技術棧不受限
比如訂單微服務和電影微服務原來都是用java寫的,現在我們想把電影微服務改成nodeJs技術,這是完全可以的,而且由于所關注的隻是電影的邏輯而已,是以技術更換的成本也就會少很多。
按需伸縮
我們上面說了單體架構在想擴充某個子產品的性能時不得不考慮到其它子產品的性能會不會受影響,對于我們微服務來講,完全不是問題,電影子產品通過什麼方式來提升性能不必考慮其它子產品的情況。
7.3 缺點
運維要求較高
對于單體架構來講,我們隻需要維護好這一個項目就可以了,但是對于微服務架構來講,由于項目是由多個微服務構成的,每個子產品出現問題都會造成整個項目運作出現異常,想要知道是哪個子產品造成的問題往往是不容易的,因為我們無法一步一步通過debug的方式來跟蹤,這就對運維人員提出了很高的要求。
分布式的複雜性
對于單體架構來講,我們可以不使用分布式,但是對于微服務架構來說,分布式幾乎是必會用的技術,由于分布式本身的複雜性,導緻微服務架構也變得複雜起來。
接口調整成本高
比如,使用者微服務是要被訂單微服務和電影微服務所調用的,一旦使用者微服務的接口發生大的變動,那麼所有依賴它的微服務都要做相應的調整,由于微服務可能非常多,那麼調整接口所造成的成本将會明顯提高。
重複勞動
對于單體架構來講,如果某段業務被多個子產品所共同使用,我們便可以抽象成一個工具類,被所有子產品直接調用,但是微服務卻無法這樣做,因為這個微服務的工具類是不能被其它微服務所直接調用的,進而我們便不得不在每個微服務上都建這麼一個工具類,進而導緻代碼的重複。
8. 微服務開發架構
目前微服務的開發架構,最常用的有以下四個:
Spring Cloud:http://projects.spring.io/spring-cloud(現在非常流行的微服務架構)
Dubbo:http://dubbo.io
Dropwizard:http://www.dropwizard.io (關注單個微服務的開發)
Consul、etcd&etc.(微服務的子產品)
9. Sprint cloud 和 Sprint boot差別
Spring Boot:
旨在簡化建立産品級的Spring應用和服務,簡化了配置檔案,使用嵌入式web伺服器,含有諸多開箱即用微服務功能,可以和spring cloud聯合部署。
Spring Cloud:
微服務工具包,為開發者提供了在分布式系統的配置管理、服務發現、斷路器、智能路由、微代理、控制總線等開發工具包。
二、微服務實踐先知
1. 用戶端如何通路這些服務?(API Gateway)
傳統的開發方式,所有的服務都是本地的,UI可以直接調用,現在按功能拆分成獨立的服務,跑在獨立的一般都在獨立的虛拟機上的 Java程序了。用戶端UI如何通路他的?背景有N個服務,前台就需要記住管理N個服務,一個服務下線/更新/更新,前台就要重新部署,這明顯不服務我們 拆分的理念,特别目前台是移動應用的時候,通常業務變化的節奏更快。另外,N個小服務的調用也是一個不小的網絡開銷。還有一般微服務在系統内部,通常是無狀态的,使用者登入資訊和權限管理最好有一個統一的地方維護管理(OAuth)。
是以,一般在背景N個服務和UI之間一般會一個代理或者叫API Gateway,他的作用包括
提供統一服務入口,讓微服務對前台透明
聚合背景的服務,節省流量,提升性能
提供安全,過濾,流控等API管理功能
我的了解其實這個API Gateway可以有很多廣義的實作辦法,可以是一個軟硬一體的盒子,也可以是一個簡單的MVC架構,甚至是一個Node.js的服務端。他們最重要的作用是為前台(通常是移動應用)提供背景服務的聚合,提供一個統一的服務出口,解除他們之間的耦合,不過API Gateway也有可能成為單點故障點或者性能的瓶頸。
2. 服務之間如何通信?(服務調用)
因為所有的微服務都是獨立的Java程序跑在獨立的虛拟機上,是以服務間的通行就是IPC(inter process communication),已經有很多成熟的方案。現在基本最通用的有兩種方式。這幾種方式,展開來講都可以寫本書,而且大家一般都比較熟悉細節了, 就不展開講了。
REST(JAX-RS,Spring Boot)
RPC(Thrift, Dubbo)
異步消息調用(Kafka, Notify)
一般同步調用比較簡單,一緻性強,但是容易出調用問題,性能體驗上也會差些,特别是調用層次多的時候。RESTful和RPC的比較也是一個很有意 思的話題。一般REST基于HTTP,更容易實作,更容易被接受,服務端實作技術也更靈活些,各個語言都能支援,同時能跨用戶端,對用戶端沒有特殊的要 求,隻要封裝了HTTP的SDK就能調用,是以相對使用的廣一些。RPC也有自己的優點,傳輸協定更高效,安全更可控,特别在一個公司内部,如果有統一個的開發規範和統一的服務架構時,他的開發效率優勢更明顯些。就看各自的技術積累實際條件,自己的選擇了。
而異步消息的方式在分布式系統中有特别廣泛的應用,他既能減低調用服務之間的耦合,又能成為調用之間的緩沖,確定消息積壓不會沖垮被調用方,同時能 保證調用方的服務體驗,繼續幹自己該幹的活,不至于被背景性能拖慢。不過需要付出的代價是一緻性的減弱,需要接受資料最終一緻性;還有就是背景服務一般要 實作幂等性,因為消息發送出于性能的考慮一般會有重複(保證消息的被收到且僅收到一次對性能是很大的考驗);最後就是必須引入一個獨立的broker,如 果公司内部沒有技術積累,對broker分布式管理也是一個很大的挑戰。
3. 這麼多服務怎麼查找?(服務發現)
在微服務架構中,一般每一個服務都是有多個拷貝,來做負載均衡。一個服務随時可能下線,也可能應對臨時通路壓力增加新的服務節點。服務之間如何互相 感覺?服務如何管理?這就是服務發現的問題了。一般有兩類做法,也各有優缺點。基本都是通過zookeeper等類似技術做服務注冊資訊的分布式管理。當 服務上線時,服務提供者将自己的服務資訊注冊到ZK(或類似架構),并通過心跳維持長連結,實時更新連結資訊。服務調用者通過ZK尋址,根據可定制算法,找到一個服務,還可以将服務資訊緩存在本地以提高性能。當服務下線時,ZK會發通知給服務用戶端。
用戶端做:優點是架構簡單,擴充靈活,隻對服務注冊器依賴。缺點是用戶端要維護所有調用服務的位址,有技術難度,一般大公司都有成熟的内部架構支援,比如Dubbo。
服務端做:優點是簡單,所有服務對于前台調用方透明,一般在小公司在雲服務上部署的應用采用的比較多。
4. 服務挂了怎麼辦?
分布式最大的特性就是網絡是不可靠 的。通過微服務拆分能降低這個風險,不過如果沒有特别的保障,結局肯定是噩夢。我們剛遇到一個線上故障就是一個很不起眼的SQL計數功能,在通路量上升 時,導緻資料庫load彪高,影響了所在應用的性能,進而影響所有調用這個應用服務的前台應用。是以當我們的系統是由一系列的服務調用鍊組成的時候,我們必須確定任一環節出問題都不至于影響整體鍊路。相應的手段有很多:
重試機制
限流
熔斷機制
負載均衡
降級(本地緩存) 這些方法基本上都很明确通用,就不詳細說明了。比如Netflix的Hystrix:https://github.com/Netflix/Hystrix
5. 微服務需要考慮的問題
這裡有一個圖非常好的總結微服務架構需要考慮的問題,包括
API Gateway
服務間調用
服務發現
服務容錯
服務部署
資料調用
三、微服務重要部件
1. 微服務基本能力
2. 服務注冊中心
服務之間需要建立一種服務發現機制,用于幫助服務之間互相感覺彼此的存在。服務啟動時會将自身的服務資訊注冊到注冊中心,并訂閱自己需要消費的服務。
服務注冊中心是服務發現的核心。它儲存了各個可用服務執行個體的網絡位址(IPAddress和Port)。服務注冊中心必須要有高可用性和實時更新功能。上面提到的 Netflix Eureka 就是一個服務注冊中心。它提供了服務注冊和查詢服務資訊的REST API。服務通過使用POST請求注冊自己的IPAddress和Port。每30秒發送一個PUT請求重新整理注冊資訊。通過DELETE請求登出服務。用戶端通過GET請求擷取可用的服務執行個體資訊。 Netflix的高可用(Netflix achieves high availability )是通過在Amazon EC2運作多個執行個體來實作的,每一個Eureka服務都有一個彈性IP Address。當Eureka服務啟動時,有DNS伺服器動态的配置設定。Eureka用戶端通過查詢 DNS來擷取Eureka的網絡位址(IP Address和Port)。一般情況下,都是傳回和用戶端在同一個可用區Eureka伺服器位址。 其他能夠作為服務注冊中心的有:
etcd —– 高可用,分布式,強一緻性的,key-value,Kubernetes和Cloud Foundry都是使用了etcd。
consul —–一個用于discovering和configuring的工具。它提供了允許用戶端注冊和發現服務的API。Consul可以進行服務健康檢查,以确定服務的可用性。
zookeeper —— 在分布式應用中被廣泛使用,高性能的協調服務。 Apache Zookeeper 最初為Hadoop的一個子項目,但現在是一個頂級項目。
2.1 zookeeper服務注冊和發現
簡單來講,zookeeper可以充當一個服務系統資料庫(Service Registry),讓多個服務提供者形成一個叢集,讓服務消費者通過服務系統資料庫擷取具體的服務通路位址(ip+端口)去通路具體的服務提供者。
具體來說,zookeeper就是個分布式檔案系統,每當一個服務提供者部署後都要将自己的服務注冊到zookeeper的某一路徑上: /{service}/{version}/{ip:port}, 比如我們的HelloWorldService部署到兩台機器,那麼zookeeper上就會建立兩條目錄:分别為/HelloWorldService/1.0.0/100.19.20.01:16888 /HelloWorldService/1.0.0/100.19.20.02:16888。
zookeeper提供了“心跳檢測”功能,它會定時向各個服務提供者發送一個請求(實際上建立的是一個 socket 長連接配接),如果長期沒有響應,服務中心就認為該服務提供者已經“挂了”,并将其剔除,比如100.19.20.02這台機器如果當機了,那麼zookeeper上的路徑就會隻剩/HelloWorldService/1.0.0/100.19.20.01:16888。
服務消費者會去監聽相應路徑(/HelloWorldService/1.0.0),一旦路徑上的資料有任務變化(增加或減少),zookeeper都會通知服務消費方服務提供者位址清單已經發生改變,進而進行更新。
更為重要的是zookeeper 與生俱來的容錯容災能力(比如leader選舉),可以確定服務系統資料庫的高可用性。
3. 負載均衡
服務高可用的保證手段,為了保證高可用,每一個微服務都需要部署多個服務執行個體來提供服務。此時用戶端進行服務的負載均衡。
3.1 負載均衡的常見政策
3.1.1 随機
把來自網絡的請求随機配置設定給内部中的多個伺服器。
3.1.2 輪詢
每一個來自網絡中的請求,輪流配置設定給内部的伺服器,從1到N然後重新開始。此種負載均衡算法适合伺服器組内部的伺服器都具有相同的配置并且平均服務請求相對均衡的情況。
3.1.3 權重輪詢
根據伺服器的不同處理能力,給每個伺服器配置設定不同的權值,使其能夠接受相應權值數的服務請求。例如:伺服器A的權值被設計成1,B的權值是3,C的權值是6,則伺服器A、B、C将分别接受到10%、30%、60%的服務請求。此種均衡算法能確定高性能的伺服器得到更多的使用率,避免低性能的伺服器負載過重。
3.1.4 IP Hash
這種方式通過生成請求源IP的哈希值,并通過這個哈希值來找到正确的真實伺服器。這意味着對于同一主機來說他對應的伺服器總是相同。使用這種方式,你不需要儲存任何源IP。但是需要注意,這種方式可能導緻伺服器負載不平衡。
3.1.5 最少連接配接數
用戶端的每一次請求服務在伺服器停留的時間可能會有較大的差異,随着工作時間加長,如果采用簡單的輪循或随機均衡算法,每一台伺服器上的連接配接程序可能會産生極大的不同,并沒有達到真正的負載均衡。最少連接配接數均衡算法對内部中需負載的每一台伺服器都有一個資料記錄,記錄目前該伺服器正在處理的連接配接數量,當有新的服務連接配接請求時,将把目前請求配置設定給連接配接數最少的伺服器,使均衡更加符合實際情況,負載更加均衡。此種均衡算法适合長時處理的請求服務,如FTP。
4. 容錯
容錯,這個詞的了解,直面意思就是可以容下錯誤,不讓錯誤再次擴張,讓這個錯誤産生的影響在一個固定的邊界之内,“千裡之堤毀于蟻穴”我們用容錯的方式就是讓這種蟻穴不要變大。那麼我們常見的降級,限流,熔斷器,逾時重試等等都是容錯的方法。
在調用服務叢集時,如果一個微服務調用異常,如逾時,連接配接異常,網絡異常等,則根據容錯政策進行服務容錯。目前支援的服務容錯政策有快速失敗,失效切換。如果連續失敗多次則直接熔斷,不再發起調用。這樣可以避免一個服務異常拖垮所有依賴于他的服務。
4.1 容錯政策
4.1.1 快速失敗
服務隻發起一次待用,失敗立即報錯。通常用于非幂等下性的寫操作
4.1.2 失效切換
服務發起調用,當出現失敗後,重試其他伺服器。通常用于讀操作,但重試會帶來更長時間的延遲。重試的次數通常是可以設定的
4.1.3 失敗安全
失敗安全, 當服務調用出現異常時,直接忽略。通常用于寫入日志等操作。
4.1.4 失敗自動恢複
當服務調用出現異常時,記錄失敗請求,定時重發。通常用于消息通知。
4.1.5 forking Cluster
并行調用多個伺服器,隻要有一個成功,即傳回。通常用于實時性較高的讀操作。可以通過forks=n來設定最大并行數。
4.1.6 廣播調用
廣播調用所有提供者,逐個調用,任何一台失敗則失敗。通常用于通知所有提供者更新緩存或日志等本地資源資訊。
5. 熔斷
熔斷技術可以說是一種“智能化的容錯”,當調用滿足失敗次數,失敗比例就會觸發熔斷器打開,有程式自動切斷目前的RPC調用,來防止錯誤進一步擴大。實作一個熔斷器主要是考慮三種模式,關閉,打開,半開。各個狀态的轉換。
我們在處理異常的時候,要根據具體的業務情況來決定處理方式,比如我們調用商品接口,對方隻是臨時做了降級處理,那麼作為網關調用就要切到可替換的服務上來執行或者擷取托底資料,給使用者友好提示。還有要區分異常的類型,比如依賴的服務崩潰了,這個可能需要花費比較久的時間來解決。也可能是由于伺服器負載臨時過高導緻逾時。作為熔斷器應該能夠甄别這種異常類型,進而根據具體的錯誤類型調整熔斷政策。增加手動設定,在失敗的服務恢複時間不确定的情況下,管理者可以手動強制切換熔斷狀态。最後,熔斷器的使用場景是調用可能失敗的遠端服務程式或者共享資源。如果是本地快取區域私有資源,使用熔斷器則會增加系統的額外開銷。還要注意,熔斷器不能作為應用程式中業務邏輯的異常處理替代品。
有一些異常比較頑固,突然發生,無法預測,而且很難恢複,并且還會導緻級聯失敗(舉個例子,假設一個服務叢集的負載非常高,如果這時候叢集的一部分挂掉了,還占了很大一部分資源,整個叢集都有可能遭殃)。如果我們這時還是不斷進行重試的話,結果大多都是失敗的。是以,此時我們的應用需要立即進入失敗狀态(fast-fail),并采取合适的方法進行恢複。
我們可以用狀态機來實作CircuitBreaker,它有以下三種狀态:
關閉( Closed ):預設情況下Circuit Breaker是關閉的,此時允許操作執行。CircuitBreaker内部記錄着最近失敗的次數,如果對應的操作執行失敗,次數就會續一次。如果在某個時間段内,失敗次數(或者失敗比率)達到門檻值,CircuitBreaker會轉換到開啟( Open )狀态。在開啟狀态中,Circuit Breaker會啟用一個逾時計時器,設這個計時器的目的是給叢集相應的時間來恢複故障。當計時器時間到的時候,CircuitBreaker會轉換到半開啟( Half-Open )狀态。
開啟( Open ):在此狀态下,執行對應的操作将會立即失敗并且立即抛出異常。
半開啟( Half-Open ):在此狀态下,Circuit Breaker會允許執行一定數量的操作。如果所有操作全部成功,CircuitBreaker就會假定故障已經恢複,它就會轉換到關閉狀态,并且重置失敗次數。如果其中 任意一次 操作失敗了,Circuit Breaker就會認為故障仍然存在,是以它會轉換到開啟狀态并再次開啟計時器(再給系統一些時間使其從失敗中恢複)
6. 限流和降級
保證核心服務的穩定性。為了保證核心服務的穩定性,随着通路量的不斷增加,需要為系統能夠處理的服務數量設定一個極限閥值,超過這個閥值的請求則直接拒絕。同時,為了保證核心服務的可用,可以對否些非核心服務進行降級,通過限制服務的最大通路量進行限流,通過管理控制台對單個微服務進行人工降級
7. SLA
SLA:Service-LevelAgreement的縮寫,意思是服務等級協定。 是關于網絡服務供應商和客戶間的一份合同,其中定義了服務類型、服務品質和客戶付款等術語。 典型的SLA包括以下項目:
配置設定給客戶的最小帶寬;
客戶帶寬極限;
能同時服務的客戶數目;
在可能影響使用者行為的網絡變化之前的通知安排;
撥入通路可用性;
運用統計學;
服務供應商支援的最小網絡利用性能,如99.9%有效工作時間或每天最多為1分鐘的停機時間;
各類客戶的流量優先權;
客戶技術支援和服務;
懲罰規定,為服務供應商不能滿足 SLA需求所指定。
8. API網關
這裡說的網關是指API網關,直面意思是将所有API調用統一接入到API網關層,有網關層統一接入和輸出。一個網關的基本功能有:統一接入、安全防護、協定适配、流量管控、長短連結支援、容錯能力。有了網關之後,各個API服務提供團隊可以專注于自己的的業務邏輯處理,而API網關更專注于安全、流量、路由等問題。
9. 多級緩存
最簡單的緩存就是查一次資料庫然後将資料寫入緩存比如redis中并設定過期時間。因為有過期失效是以我們要關注下緩存的穿透率,這個穿透率的計算公式,比如查詢方法queryOrder(調用次數1000/1s)裡面嵌套查詢DB方法queryProductFromDb(調用次數300/s),那麼redis的穿透率就是300/1000,在這種使用緩存的方式下,是要重視穿透率的,穿透率大了說明緩存的效果不好。還有一種使用緩存的方式就是将緩存持久化,也就是不設定過期時間,這個就會面臨一個資料更新的問題。一般有兩種辦法,一個是利用時間戳,查詢預設以redis為主,每次設定資料的時候放入一個時間戳,每次讀取資料的時候用系統目前時間和上次設定的這個時間戳做對比,比如超過5分鐘,那麼就再查一次資料庫。這樣可以保證redis裡面永遠有資料,一般是對DB的一種容錯方法。還有一個就是真正的讓redis做為DB使用。就是圖裡面畫的通過訂閱資料庫的binlog通過資料異構系統将資料推送給緩存,同時将将緩存設定為多級。可以通過使用jvmcache作為應用内的一級緩存,一般是體積小,通路頻率大的更适合這種jvmcache方式,将一套redis作為二級remote緩存,另外最外層三級redis作為持久化緩存。
10. 逾時和重試
逾時與重試機制也是容錯的一種方法,凡是發生RPC調用的地方,比如讀取redis,db,mq等,因為網絡故障或者是所依賴的服務故障,長時間不能傳回結果,就會導緻線程增加,加大cpu負載,甚至導緻雪崩。是以對每一個RPC調用都要設定逾時時間。對于強依賴RPC調用資源的情況,還要有重試機制,但是重試的次數建議1-2次,另外如果有重試,那麼逾時時間就要相應的調小,比如重試1次,那麼一共是發生2次調用。如果逾時時間配置的是2s,那麼用戶端就要等待4s才能傳回。是以重試+逾時的方式,逾時時間要調小。這裡也再談一下一次PRC調用的時間都消耗在哪些環節,一次正常的調用統計的耗時主要包括: ①調用端RPC架構執行時間 + ②網絡發送時間 + ③服務端RPC架構執行時間 + ④服務端業務代碼時間。調用方和服務方都有各自的性能監控,比如調用方tp99是500ms,服務方tp99是100ms,找了網絡組的同僚确認網絡沒有問題。那麼時間都花在什麼地方了呢,兩種原因,用戶端調用方,還有一個原因是網絡發生TCP重傳。是以要注意這兩點。
11. 線程池隔離
在抗量這個環節,Servlet3異步的時候,有提到過線程隔離。線程隔離的之間優勢就是防止級聯故障,甚至是雪崩。當網關調用N多個接口服務的時候,我們要對每個接口進行線程隔離。比如,我們有調用訂單、商品、使用者。那麼訂單的業務不能夠影響到商品和使用者的請求處理。如果不做線程隔離,當通路訂單服務出現網絡故障導緻延時,線程積壓最終導緻整個服務CPU負載滿。就是我們說的服務全部不可用了,有多少機器都會被此刻的請求塞滿。那麼有了線程隔離就會使得我們的網關能保證局部問題不會影響全局。