《微服務設計》,Building Microservices,作者Sam Newman,譯者崔力強、張駿,人民郵電出版社,2016年。
筆記中有些內容直接引用原書。
================================================================
第十一章規模化微服務
1.故障無處不在
要假定故障會發生,以這種想法來處理每一件事情,為故障做好準備。
2.多少是太多
不同的系統對容忍的故障程度、系統的響應速度等都是不一樣的,需要根據這些因素選擇技術,需要讓客戶了解不同級別的服務對應的成本。
響應時間/延遲。可以用不同數量的用戶來測量。“我期望這個網站,當每秒處理200個并發連接時,90%的響應時間在2秒以內。”
可用性。服務能出現故障嗎?是24/7服務嗎?
數據持久性。多大比例的數據丟失是可接受的?數據應該保存多久?
3.功能降級
功能降級的需求要從業務角度出發。
4.架構性安全措施
架構性安全性措施是一些組合的模式,確保如果事情真的出錯了,不會引起嚴重的級聯影響。舉了一個之前網站由于下游服務響應緩慢導致級聯故障的例子,采取的措施是:正確的設置超時;實現艙壁隔離不同的連接池;實現一個斷路器。
5.反脆弱的組織
《反脆弱》一書的作者Nassim Taleb認為事物實際上受益于失敗和混亂。Netflix通過引發故障來確保其系統的容錯性。Netflix開源了混亂猴子(Chaos Monkey)、混亂大猩猩(Chaos Gorilla)以及延遲猴子(Latency Monkey)項目,它們分別用于模擬關閉服務器、關閉可用區以及網絡延遲。讓軟件擁抱和引發故障,從失敗中學習。
超時。下游服務超時,等待太長來決定調用失敗,整個系統會變慢。太短會讓正常工作的調用被認為失敗。沒有超時,一個宕掉的下游系統會讓整個系統掛起。要給所有跨進程調用設置超時,并選擇一個默認時間。超時發生,記錄日志。
斷路器。當對下游資源的請求發生一定數量的失敗后,打開斷路器,請求會迅速失敗。過段時間再發送檢查,如果下游服務恢復正常則關掉斷路器。一些斷路器實現:Netflix的Hystrix庫(基于JVM),.NET的Polly,Ruby的circuit_breaker mixin。
艙壁。艙壁通過隔離避免級聯故障。在第4節的例子中,就是對不同服務使用了不同的連接池來隔離。還可以使用關注點分離,通過把功能分離成獨立的微服務,隔離故障影響。斷路器也算蜜蜂艙壁的一種自動機制。
隔離。服務間加強隔離,最好能做到上游服務可以允許下游服務離線。
6.冪等
多次執行同一操作,與執行一次該操作效果相同。使消息的處理成為冪等能減少很多工作量。
7.擴展
擴展系統的原因:處理失敗;提升性能。
更強大的主機。這是垂直擴展。
拆分負載。單服務單主機模式擴展為多服務多主機模式。
分散風險。不要把雞蛋放在一個籃子里。例如,不要把多個服務放在一臺主機上。要考慮SAN的故障,它會引起所有虛擬機不可用。不要讓所有服務運行在同一個數據中心的同一個機架上。了解云服務商的SLA(Service-Level Agreement)保證,確保和自己的需求相匹配。
負載均衡。使用負載均衡器,有硬件有軟件(如mod_proxy)。能提供SSL終止功能(將HTTPS連接在內部轉換為HTTP連接)的能簡化單個主機運行實例的配置。負載均衡器的配置要和服務的配置一樣通過版本管理系統管理起來,要能自動化應用。
基于worker的系統。該系統也可以像負載均衡一樣分擔負載降低脆弱性。通過待辦作業列表來分配作業到不同的worker,需要一個持久化的消息代理或Zookeeper。
重新設計。前期將精力放在更重要的事情上,當確實有大量負載來了,再去考慮解決,必要時重新設計。
8.擴展數據庫
服務的可用性和數據的可用性。要意識到二者的區別。
擴展讀取。通過主節點副本可以做到擴展讀取,不過建議先采用緩存(簡單并且性能改善顯著),其次才考慮副本。
擴展寫操作。數據分片解決擴展寫。看看Cassandra、Mongo或者Riak這樣的數據庫系統。
共享數據庫基礎設施。可能引起單點故障。
CQRS。命令查詢職責分離(Command-Query Responsibility Segregation)模式,系統一部分負責獲取修改狀態的請求命令并處理它,另一部分負責處理查詢。內部用于處理命令和查詢的模型是完全獨立的,可以使用不同的服務或在不同的硬件實現,可以使用不同的數據存儲,因此擴展性強。
9.緩存
客戶端、代理和服務器端緩存。代理緩存可以用反響代理或CDN;服務器端緩存可以用Redis或Memcache。
HTTP緩存。cache-control指令,Expires頭部,實體標簽Etag。它們的使用可以參考《REST實戰》。
為寫使用緩存。有爆發式寫操作或同樣數據可能被寫入多次,后寫式緩存很有作用。
為彈性使用緩存。在服務不可用時可以使用緩存代替一些服務。
隱藏源服務。緩存消失時,如果源服務無法應對大量請求,則不要請求。可以由源服務在后臺重建緩存,也可以原始請求快速失敗,避免級聯故障。
保持簡單。避免太多地方使用緩存。
緩存中毒:一個警示。需要了解數據從數據源到終點的完整緩存路徑,從而真正理解它的復雜性以及使它出錯的原因。
10.自動收縮
可以根據負載趨勢變化來觸發自動收縮,以響應負載。另外,自動伸縮被更多應用于響應故障。
11.CAP定理
一致性(consistency)、可用性(availability)、分區容忍性(partition tolerance),三個中最多只能保證兩個。
犧牲一致性。
犧牲可用性。
犧牲分區容忍性。
AP還是CP。需要根據具體情況權衡。
這不是全部或全不。不同的服務可以是不同的性質,有的是AP,有的是CP,并不是整個系統中的所有服務都保持一致。
真實世界。在真實世界中,無論系統本身如何一致,都難以做到和真實世界的一致。因此,很多情況下,AP系統是最終正確的選擇。
12.服務發現
服務實例要能夠注冊,其它服務要能夠找到已經注冊的實例。
DNS。可以使用DNS解決方案。更新DNS條目有些痛苦。亞馬遜的Route53做的不錯,但可選的自托管服務中,沒有那么好的。
13.動態服務注冊
比DNS更適用于高度動態的環境發現節點。
Zookeeper。被用于很多場景:配置管理、服務間的數據同步、leader選舉、消息隊列和命名服務。Zookeeper能確保數據在多個節點之間安全地復制,并且當節點故障后仍能保持一致性。
Consul。也支持配置管理和服務發現,其殺手級特性之一是提供了現成的DNS服務器。還具有對節點進行健康檢查的能力。
Eureka。Netflix開源的系統,提供了基本的負載均衡功能,支持服務實例的基本輪訓和調度查找。
構造你自己的系統。可以使用AWS的API來構造。
別忘了人。要有工具能在注冊中心上顯示報告和儀表盤給人看。
14.文檔服務
要能生成服務的API文檔。
Swagger。提供的終端用戶體驗不錯,為超媒體核心中的增量探索概念做的很少。
HAL和HAL瀏覽器。Hypertext Application Language是一個描述公開的超媒體控制的標準。在使用超媒體的話,建議用HAL,沒使用的話建議用Swagger。
15.自描述系統
Martin Fowler提出的人文注冊表方法,有一個地方可以讓人們記錄組織中有關服務的信息,和維基一樣簡單。從活系統中抽取一些數據,形成靜態Web頁面或維基是一個好的開始。
16.小結
推薦Nygard的書《Release It!》,分享了關于系統故障的故事以及處理它們的模式,對于構建任何規模化系統都強烈推薦。