首先,咱們須要闡述一下爲何須要分佈式系統,而不是傳統的單體架構。也許這對你來講已經不是什麼問題了,可是請容許我在這裏從新說明一下。使用分佈式系統主要有兩方面緣由。html
增大系統容量。咱們的業務量愈來愈大,而要能應對愈來愈大的業務量,一臺機器的性能已經沒法知足了,咱們須要多臺機器才能應對大規模的應用場景。因此,咱們須要垂直或是水平拆分業務系統,讓其變成一個分佈式的架構。前端
增強系統可用。咱們的業務愈來愈關鍵,須要提升整個系統架構的可用性,這就意味着架構中不能存在單點故障。這樣,整個系統不會由於一臺機器出故障而致使總體不可用。因此,須要經過分佈式架構來冗餘系統以消除單點故障,從而提升系統的可用性。git
固然,分佈式系統還有一些優點,好比:程序員
由於軟件服務模塊被拆分,開發和發佈速度能夠並行而變得更快;github
系統擴展性更高;web
團隊協做流程也會獲得改善;算法
……spring
不過,這個世界上不存在完美的技術方案,採用任何技術方案都是「按下葫蘆浮起瓢」,都是有得有失,都是一種trade-off。也就是說,分佈式系統在消除上述問題的同時,也給咱們帶來了其餘的問題。所以,咱們須要清楚地知道分佈式系統所帶來的問題。shell
下面這個表格比較了單體應用和分佈式架構的優缺點。數據庫
從上面的表格咱們能夠看到,分佈式系統雖然有一些優點,但也存在一些問題。
架構設計變得複雜(尤爲是其中的分佈式事務)。
部署單個服務會比較快,可是若是一次部署須要多個服務,部署會變得複雜。
系統的吞吐量會變大,可是響應時間會變長。
運維複雜度會由於服務變多而變得很複雜。
架構複雜致使學習曲線變大。
測試和查錯的複雜度增大。
技術能夠不少樣,這會帶來維護和運維的複雜度。
管理分佈式系統中的服務和調度變得困難和複雜。
也就是說,分佈式系統架構的難點在於系統設計,以及管理和運維。因此,分佈式架構解決了「單點」和「性能容量」的問題,但卻新增了一堆問題。而對於這些新增的問題,還會衍生出更多的子問題,這就須要咱們不斷地用各式各樣的技術和手段來解決這些問題。
這就出現了我前面所說的那些架構方式,以及各類相關的管理型的技術方法。這個世界就是這樣變得複雜起來的。
從20世紀70年代的模塊化編程,80年代的面向事件設計,90年代的基於接口/構件設計,這個世界很天然地演化出了SOA——基於服務的架構。SOA架構是構造分佈式計算應用程序的方法。它將應用程序功能做爲服務發送給最終用戶或者其餘服務。它採用開放標準與軟件資源進行交互,並採用標準的表示方式。
開發、維護和使用SOA要遵循如下幾條基本原則。
但IBM搞出來的SOA很是重,因此對SOA的裁剪和優化歷來沒有中止過。好比,以前的SOAP、WSDL和XML這樣的東西基本上已經被拋棄了,而改爲了RESTful和JSON這樣的方式。而ESB(Enterprise Service Bus,企業服務總線)這樣很是重要的東西也被簡化成了Pub/Sub的消息服務……
不過,SOA的思想一直延續着。因此,咱們如今也不說SOA了,而是說分佈式服務架構了。
下面是一個SOA架構的演化圖。
咱們能夠看到,面向服務的架構有如下三個階段。
20世紀90年代前,是單體架構,軟件模塊高度耦合。固然,這張圖一樣也說明了有的SOA架構其實和單體架構沒什麼兩樣,由於都是高度耦合在一塊兒的。就像圖中的齒輪同樣,當你調用一個服務時,這個服務會調用另外一個服務,而後又調用另外的服務……因而整個系統就轉起來了。可是這本質是比較耦合的作法。
而2000年左右出現了比較鬆耦合的SOA架構,這個架構須要一個標準的協議或是中間件來聯動其它相關聯的服務(如ESB)。這樣一來,服務間並不直接依賴,而是經過中間件的標準協議或是通信框架相互依賴。這其實就是IoC(控制反轉)和DIP(依賴倒置原則)的設計思想在架構中的實踐。它們都依賴於一個標準的協議或是一個標準統一的交互方式,而不是直接調用。
而2010年後,出現了微服務架構,這個架構更爲鬆耦合。每個微服務都能獨立完整地運行(所謂的自包含),後端單體的數據庫也被微服務這樣的架構分散到不一樣的服務中。而它和傳統SOA的差異在於,服務間的整合須要一個服務編排或是服務整合的引擎。就好像交響樂中須要有一個指揮來把全部樂器編排和組織在一塊兒。
通常來講,這個編排和組織引擎能夠是工做流引擎,也能夠是網關。固然,還須要輔助於像容器化調度這樣的技術方式,如Kubernetes。在Martin Fowler 的 Microservices 這篇文章中有詳細描述。
微服務的出現使得開發速度變得更快,部署快,隔離性高,系統的擴展度也很好,可是在集成測試、運維和服務管理等方面就比較麻煩了。因此,須要一套比較好的微服務PaaS平臺。就像Spring Cloud同樣須要提供各類配置服務、服務發現、智能路由、控制總線……還有像Kubernetes提供的各式各樣的部署和調度方式。
沒有這些PaaS層的支撐,微服務也是很難被管理和運維的。好在今天的世界已經有具有了這些方面的基礎設施,因此,採用微服務架構,我認爲只是一個時間問題了。
從目前能夠獲得的信息來看,對分佈式服務化架構實踐最先的應該是亞馬遜。由於早在2002年的時候,亞馬遜CEO傑夫·貝索斯(Jeff Bezos)就向全公司頒佈了下面的這幾條架構規定(來自《Steve Yegge對Google平臺吐槽》一文)。
全部團隊的程序模塊都要經過Service Interface方式將其數據與功能開放出來。
團隊間程序模塊的信息通訊,都要經過這些接口。
除此以外沒有其它的通訊方式。其餘形式一律不容許:不能直接鏈結別的程序(把其餘團隊的程序當作動態連接庫來連接),不能直接讀取其餘團隊的數據庫,不能使用共享內存模式,不能使用別人模塊的後門,等等。惟一容許的通訊方式是調用Service Interface。
任何技術均可以使用。好比:HTTP、CORBA、Pub/Sub、自定義的網絡協議等。
全部的Service Interface,毫無例外,都必須從骨子裏到表面上設計成能對外界開放的。也就是說,團隊必須作好規劃與設計,以便將來把接口開放給全世界的程序員,沒有任何例外。
不這樣作的人會被炒魷魚。
這應該就是AWS(Amazon Web Service)出現的基因吧。固然,前面說過,採用分佈式系統架構後會出現不少的問題。好比:
爲了克服這些問題,亞馬遜這麼多年的實踐讓其能夠運維和管理極其複雜的分佈式服務架構。我以爲主要有如下幾點。
分佈式服務的架構須要分佈式的團隊架構。在亞馬遜,一個服務由一個小團隊(Two Pizza Team不超過16我的,兩張Pizza能夠餵飽的團隊)負責,從前端負責到數據,從需求分析負責到上線運維。這是良性的分工策略——按職責分工,而不是按技能分工。
分佈式服務查錯不容易。一旦出現比較嚴重的故障,須要總體查錯。出現一個S2的故障,就能夠看到每一個團隊的人都會上線。在工單系統裏能看到,在故障發生的一開始,你們都在簽到並自查本身的系統。若是沒問題,也要在線待命(standby),等問題解決。(我在《故障處理最佳實踐:應對故障》一文中詳細地講過這個事)。
沒有專職的測試人員,也沒有專職的運維人員,開發人員作全部的事情。開發人員作全部事情的好處是——吃本身的狗糧(Eat Your Own Dog Food) 最微觀的實踐。本身寫的代碼本身維護本身養,會讓開發人員明白,寫代碼容易維護代碼複雜。這樣,開發人員在接需求、作設計、寫代碼、作工具時都會考慮到軟件的長期維護性。
運維優先,崇尚簡化和自動化。爲了可以運維如此複雜的系統,亞馬遜內部在運維上下了很是大的功夫。如今人們所說的DevOps這個事,亞馬遜在10多年前就作到了。亞馬遜最爲強大的就是運維,拼命地對系統進行簡化和自動化,讓亞馬遜作到了能夠輕鬆運維擁有上千萬臺虛機的AWS雲平臺。
內部服務和外部服務一致。不管是從安全方面,仍是接口設計方面,不管是從運維方面,仍是故障處理的流程方面,亞馬遜的內部系統都和外部系統同樣對待。這樣作的好處是,內部系統的服務隨時均可以開放出來。並且,從第一天開始,服務提供方就有對外服務的能力。能夠想像,以這樣的標準運做的團隊其能力會是什麼樣的。
在進化的過程當中,亞馬遜遇到的問題不少,甚至還有不少幾乎沒有人會想到的很是生僻的東西,它都一一學習和總結了,並且都解決得很好。
構建分佈式系統很是難,充滿了各類各樣的問題,但亞馬遜仍是絕不猶豫地走了下去。這是由於亞馬遜想作平臺,不是「像淘寶這樣的中介式流量平臺」,而是那種「能夠對外輸出能力的平臺」。
亞馬遜以爲本身沒有像史蒂夫·喬布斯(Steve Jobs)這樣的牛人,不可能作出像iPhone這樣的爆款產品,並且用戶天生就是衆口難調,與其作一個你們都不滿意的軟件,還不如把一些基礎能力對外輸出,引入外部的力量來一塊兒完成一個用戶滿意的產品。這其實就是在創建本身的生態圈。雖然在今天看來這個事已經不稀奇了,可是貝索斯早在十五年前就悟到了,實在是個天才。
因此,分佈式服務架構是須要從組織,到軟件工程,再到技術上的一個大的改造,須要比較長的時間來磨合和改進,並不斷地總結教訓和成功經驗。
咱們再來看一下分佈式系統在技術上須要注意的問題。
這主要表如今:
不一樣的軟件,不一樣的語言會出現不一樣的兼容性和不一樣的開發、測試、運維標準。不一樣的標準會讓咱們用不一樣的方式來開發和運維,引發架構複雜度的提高。好比:有的軟件修改配置要改它的.conf文件,而有的則是調用管理API接口。
在通信方面,不一樣的軟件用不一樣的協議,就算是相同的網絡協議裏也會出現不一樣的數據格式。還有,不一樣的團隊由於用不一樣的技術,會有不一樣的開發和運維方式。這些不一樣的東西,會讓咱們的整個分佈式系統架構變得異常複雜。因此,分佈式系統架構須要有相應的規範。
好比,我看到,不少服務的API出錯不返回HTTP的錯誤狀態碼,而是返回個正常的狀態碼200,而後在HTTP Body裏的JSON字符串中寫着個:error,bla bla error message。這簡直就是一種反人類的作法。我實在不明白爲何會有衆多這樣的設計。這讓監控怎麼作啊?如今,你應該使用Swagger的規範了。
再好比,我看到不少公司的軟件配置管理裏就是一個key-value的東西,這樣的東西靈活到能夠很容易地被濫用。不規範的配置命名,不規範的值,甚至在配置中直接嵌入前端展現內容……
一個好的配置管理,應該分紅三層:底層和操做系統相關,中間層和中間件相關,最上面和業務應用相關。因而底層和中間層是不能讓用戶靈活修改的,而是隻讓用戶選擇。好比:操做系統的相關配置應該造成模板來讓人選擇,而不是讓人亂配置的。只有配置系統造成了規範,咱們才hold得住衆多的系統。
再好比:數據通信協議。一般來講,做爲一個協議,必定要有協議頭和協議體。協議頭定義了最基本的協議數據,而協議體纔是真正的業務數據。對於協議頭,咱們須要很是規範地讓每個使用這個協議的團隊都使用一套標準的方式來定義,這樣咱們才容易對請求進行監控、調度和管理。
這樣的規範還有不少,我在這就不一一列舉了。
對於傳統的單體應用,一臺機器掛了,整個軟件就掛掉了。可是你千萬不要覺得在分佈式的架構下不會發生這樣的事。分佈式架構下,服務是會有依賴的,因而一個服務依賴鏈上,某個服務掛掉了,會致使出現「多米諾骨牌」效應,會倒一片。
因此,在分佈式系統中,服務的依賴也會帶來一些問題。
這是服務治理的內容了。服務治理不但須要咱們定義出服務的關鍵程度,還須要咱們定義或是描述出關鍵業務或服務調用的主要路徑。沒有這個事情,咱們將沒法運維或是管理整個系統。
這裏須要注意的是,不少分佈式架構在應用層上作到了業務隔離,然而,在數據庫結點上並無。若是一個非關鍵業務把數據庫拖死,那麼會致使全站不可用。因此,數據庫方面也須要作相應的隔離。也就是說,最好一個業務線用一套本身的數據庫。這就是亞馬遜服務器的實踐——系統間不能讀取對方的數據庫,只經過服務接口耦合。這也是微服務的要求。咱們不但要拆分服務,還要爲每一個服務拆分相應的數據庫。
在分佈式系統中,由於使用的機器和服務會很是多,因此,故障發生的頻率會比傳統的單體應用更大。只不過,單體應用的故障影響面很大,而分佈式系統中,雖然故障的影響面能夠被隔離,可是由於機器和服務多,出故障的頻率也會多。另外一方面,由於管理複雜,並且沒人知道整個架構中有什麼,因此很是容易犯錯誤。
你會發現,對分佈式系統架構的運維,簡直就是一場噩夢。咱們會慢慢地明白下面這些道理。
運維團隊在分佈式系統下會很是忙,忙到每時每刻都要處理大大小小的故障。我看到,不少大公司,都在本身的系統裏拼命地添加各類監控指標,有的可以添加出幾萬個監控指標。我以爲這徹底是在「使蠻力」。一方面,信息太多等於沒有信息,另外一方面,SLA要求咱們定義出「Key Metrics」,也就是所謂的關鍵指標。然而,他們卻沒有。這實際上是一種思惟上的懶惰。
可是,上述的都是在「救火階段」而不是「防火階段」。所謂「防火勝於救火」,咱們還要考慮如何防火,這須要咱們在設計或運維繫統時都要爲這些故障考慮,即所謂 Design for Failure。在設計時就要考慮如何減輕故障。若是沒法避免,也要使用自動化的方式恢復故障,減小故障影響面。
由於當機器和服務數量愈來愈多時,你會發現,人類的缺陷就成爲了瓶頸。這個缺陷就是人類沒法對複雜的事情作到事無鉅細的管理,只有機器自動化才能幫助人類。 也就是,人管代碼,代碼管機器,人無論機器!
一般來講,咱們能夠把系統分紅四層:基礎層、平臺層、應用層和接入層。
對於這四層,咱們須要知道:
不少公司都是按技能分工是,把技術團隊分爲產品開發、中間件開發、業務運維、系統運維等子團隊。這樣的分工緻使各管一攤,不少事情徹底連不在一塊兒。整個系統會像 「多米諾骨牌」同樣,一個環節出現問題,就會倒下去一大片。由於沒有一個統一的運維視圖,不知道一個服務調用是如何通過每個服務和資源,也就致使咱們在出現故障時要花大量的時間在溝通和定位問題上。
以前我在某雲平臺的一次經歷就是這樣的。從接入層到負載均衡,再到服務層,再到操做系統底層,設置的KeepAlive的參數徹底不一致,致使用戶發現,軟件運行的行爲和文檔中定義的徹底不同。工程師查錯的過程簡直就是一場惡夢,覺得找到了一個,結果還有一個,來來回回花了大量的時間才把全部KeepAlive的參數設置成一致的,浪費了太多的時間。
分工不是問題,問題是分工後的協做是否統一和規範。這點,你必定要重視。
好了,咱們來總結一下今天分享的主要內容。首先,我以亞馬遜爲例,講述了它是如何作分佈式服務架構的,遇到了哪些問題,以及是如何解決的。我認爲,亞馬遜在分佈式服務系統方面的這些實踐和經驗積累,是AWS出現的基因。隨後分享了在分佈式系統中須要注意的幾個問題,同時給出了應對方案。
我認爲,構建分佈式服務須要從組織,到軟件工程,再到技術上的一次大的改造,須要比較長的時間來磨合和改進,並不斷地總結教訓和成功經驗。下篇文章中,咱們講述分佈式系統的技術棧。但願對你有幫助。
正如咱們前面所說的,構建分佈式系統的目的是增長系統容量,提升系統的可用性,轉換成技術方面,也就是完成下面兩件事。
說白了就是幹兩件事。一是提升總體架構的吞吐量,服務更多的併發和流量,二是爲了提升系統的穩定性,讓系統的可用性更高。
我們先來看看,提升系統性能的經常使用技術。
緩存系統。加入緩存系統,能夠有效地提升系統的訪問能力。從前端的瀏覽器,到網絡,再到後端的服務,底層的數據庫、文件系統、硬盤和CPU,全都有緩存,這是提升快速訪問能力最有效的手段。對於分佈式系統下的緩存系統,須要的是一個緩存集羣。這其中須要一個Proxy來作緩存的分片和路由。
負載均衡系統,是作水平擴展的關鍵技術。其能夠用多臺機器來共同分擔一部分流量請求。
異步調用。異步系統主要經過消息隊列來對請求作排隊處理,這樣能夠把前端的請求的峯值給「削平」了,然後端經過本身可以處理的速度來處理請求。這樣能夠增長系統的吞吐量,可是實時性就差不少了。同時,還會引入消息丟失的問題,因此要對消息作持久化,這會形成「有狀態」的結點,從而增長了服務調度的難度。
對於通常公司來講,在初期,會使用讀寫分離的數據鏡像方式,然後期會採用分庫分表的方式。
接下來,我們來看看提升系統系統穩定性的一些經常使用技術。
服務拆分,主要有兩個目的:一是爲了隔離故障,二是爲了重用服務模塊。但服務拆分完以後,會引入服務調用間的依賴問題。
服務冗餘,是爲了去除單點故障,並能夠支持服務的彈性伸縮,以及故障遷移。然而,對於一些有狀態的服務來講,冗餘這些有狀態的服務帶來了更高的複雜性。其中一個是彈性伸縮時,須要考慮數據的複製或是從新分片,遷移的時候還要遷移數據到其它機器上。
限流降級。當系統實在扛不住壓力時,只能經過限流或者功能降級的方式來停掉一部分服務,或是拒絕一部分用戶,以確保整個架構不會掛掉。這些技術屬於保護措施。
高可用架構,一般來講是從冗餘架構的角度來保障可用性。好比,多租戶隔離,災備多活,或是數據能夠在其中複製保持一致性的集羣。總之,就是爲了避免出單點故障。
高可用運維,指的是DevOps中的CI(持續集成)/CD(持續部署)。一個良好的運維應該是一條很流暢的軟件發佈管線,其中作了足夠的自動化測試,還能夠作相應的灰度發佈,以及對線上系統的自動化控制。這樣,能夠作到「計劃內」或是「非計劃內」的宕機事件的時長最短。
上述這些技術很是有技術含量,並且須要投入大量的時間和精力。
而經過上面的分析,咱們能夠看到,引入分佈式系統,會引入一堆技術問題,須要從如下幾個方面來解決。
服務治理。服務拆分、服務調用、服務發現,服務依賴,服務的關鍵度定義……服務治理的最大意義是須要把服務間的依賴關係、服務調用鏈,以及關鍵的服務給梳理出來,並對這些服務進行性能和可用性方面的管理。
架構軟件管理。服務之間有依賴,並且有兼容性問題,因此,總體服務所造成的架構須要有架構版本管理、總體架構的生命週期管理,以及對服務的編排、聚合、事務處理等服務調度功能。
DevOps。分佈式系統能夠更爲快速地更新服務,可是對於服務的測試和部署都會是挑戰。因此,還須要DevOps的全流程,其中包括環境構建、持續集成、持續部署等。
自動化運維。有了DevOps後,咱們就能夠對服務進行自動伸縮、故障遷移、配置管理、狀態管理等一系列的自動化運維技術了。
資源調度管理。應用層的自動化運維須要基礎層的調度支持,也就是雲計算IaaS層的計算、存儲、網絡等資源調度、隔離和管理。
總體架構監控。若是沒有一個好的監控系統,那麼自動化運維和資源調度管理只可能成爲一個泡影,由於監控系統是你的眼睛。沒有眼睛,沒有數據,就沒法進行高效的運維。因此說,監控是很是重要的部分。這裏的監控須要對三層系統(應用層、中間件層、基礎層)進行監控。
流量控制。最後是咱們的流量控制,負載均衡、服務路由、熔斷、降級、限流等和流量相關的調度都會在這裏,包括灰度發佈之類的功能也在這裏。
此時,你會發現,要作好這麼多的技術,或是要具有這麼多的能力,簡直就是一個門檻,是一個成本巨高無比的技術棧,看着就都頭暈。要實現出來得投入多少人力、物力和時間啊。是的,這就是分佈式系統中最大的坑。
不過,咱們應該慶幸本身生活在了一個很是不錯的年代。今天有一個技術叫——Docker,經過Docker以及其衍生出來的Kubernetes 之類的軟件或解決方案,大大地下降了作上面不少事情的門檻。Docker把軟件和其運行的環境打成一個包,而後比較輕量級地啓動和運行。在運行過程當中,由於軟件變成了服務可能會改變現有的環境。可是不要緊,當你從新啓動一個Docker的時候,環境又會變成初始化狀態。
這樣一來,咱們就能夠利用Docker的這個特性來把軟件在不一樣的機器上進行部署、調度和管理。若是沒有Docker或是Kubernetes,那麼你能夠認爲咱們還活在「原始時代」。如今你知道爲何Docker這樣的容器化虛擬化技術是將來了吧。由於分佈式系統已是徹底不可逆轉的技術趨勢了。
可是,上面還有不少的技術是Docker及其周邊技術沒有解決的,因此,依然還有不少事情要作。那麼,若是是一個一個地去作這些技術的話,就像是咱們在撐開一張網裏面一個一個的網眼,本質上這是使蠻力的作法。咱們但願能夠找到系統的「綱」,一把就能張開整張網。那麼,這個綱在哪裏呢?
總結一下上面講述的內容,你不難發現,分佈式系統有五個關鍵技術,它們是:
而最後一項——開發和運維的自動化,是須要把前四項都作到了,纔有可能實現的。因此,最爲關鍵是下面這四項技術,即應用總體監控、資源和服務調度、狀態和數據調度及流量調度,它們是構建分佈式系統最最核心的東西。
後面的文章中,我會一項一項地解析這些關鍵技術。
回顧一下今天的要點內容。首先,我總結了分佈式系統須要乾的兩件事:一是提升總體架構的吞吐量,服務更多的併發和流量,二是爲了提升系統的穩定性,讓系統的可用性更高。而後分別從這兩個方面闡釋,須要經過哪些技術來實現,並梳理出其中的技術難點及可能會帶來的問題。最後,歡迎你分享一下你在解決系統的性能和可用性方面使用到的方法和技巧。
雖然Docker及其衍生出來的Kubernetes等軟件或解決方案,能極大地下降不少事兒的門檻。但它們沒有解決的問題還有不少,須要掌握分佈式系統的五大關鍵技術,從根本上解決問題。後面我將陸續撰寫幾篇文章一一闡述這幾大關鍵技術,詳見文末給出的《分佈式系統架構的本質》系列文章的目錄。
首先,咱們須要一個全棧系統監控的東西。它就像是咱們的眼睛,沒有它,咱們就不知道系統到底發生了什麼,咱們將沒法管理或是運維整個分佈式系統。因此,這個系統是很是很是關鍵的。
而在分佈式或Cloud Native的狀況下,系統分紅多層,服務各類關聯,須要監控的東西特別多。沒有一個好的監控系統,咱們將沒法進行自動化運維和資源調度。
這個監控系統須要完成的功能爲:
所謂全棧監控,其實就是三層監控。
基礎層:監控主機和底層資源。好比:CPU、內存、網絡吞吐、硬盤I/O、硬盤使用等。
中間層:就是中間件層的監控。好比:Nginx、Redis、ActiveMQ、Kafka、MySQL、Tomcat等。
應用層:監控應用層的使用。好比:HTTP訪問的吞吐量、響應時間、返回碼,調用鏈路分析,性能瓶頸,還包括用戶端的監控。
這還須要一些監控的標準化。
這裏還要多說一句,如今咱們的不少監控系統都作得很很差,它們主要有兩個很大的問題。
監控數據是隔離開來的。由於公司分工的問題,開發、應用運維、系統運維,各管各的,因此不少公司的監控系統也是各是各的,徹底串不起來。
監控的數據項太多。有些公司的運維團隊把監控的數據項多作爲一個亮點處處講,好比監控指標達到5萬多個。老實說,這太丟人了。由於信息太多等於沒有信息,抓不住重點的監控纔會作成這個樣子,徹底就是使蠻力的作法。
一個好的監控系統應該有如下幾個特徵。
關注於總體應用的SLA。主要從爲用戶服務的API來監控整個系統。
關聯指標聚合。 把有關聯的系統及其指標聚合展現。主要是三層系統數據:基礎層、平臺中間件層和應用層。其中,最重要的是把服務和相關的中間件以及主機關聯在一塊兒,服務有可能運行在Docker中,也有可能運行在微服務平臺上的多個JVM中,也有可能運行在Tomcat中。總之,不管運行在哪裏,咱們都須要把服務的具體實例和主機關聯在一塊兒,不然,對於一個分佈式系統來講,定位問題猶如大海撈針。
快速故障定位。 對於現有的系統來講,故障老是會發生的,並且還會頻繁發生。故障發生不可怕,可怕的是故障的恢復時間過長。因此,快速地定位故障就至關關鍵。快速定位問題須要對整個分佈式系統作一個用戶請求跟蹤的trace監控,咱們須要監控到全部的請求在分佈式系統中的調用鏈,這個事最好是作成沒有侵入性的。
換句話說,一個好的監控系統主要是爲如下兩個場景所設計的。
容量管理。 提供一個全局的系統運行時數據的展現,可讓工程師團隊知道是否須要增長機器或者其它資源。
性能管理。能夠經過查看大盤,找到系統瓶頸,並有針對性地優化系統和相應代碼。
定位問題。能夠快速地暴露並找到問題的發生點,幫助技術人員診斷問題。
性能分析。當出現非預期的流量提高時,能夠快速地找到系統的瓶頸,並能夠幫助開發人員深刻代碼。
只有作到了上述的這些才能是一個好的監控系統。
下面是我認爲一個好的監控系統應該實現的東西。
以下圖所示(截圖來自我作的一個APM的監控系統)。
服務調用時長分佈。使用Zipkin,能夠看到一個服務調用鏈上的時間分佈,這樣有助於咱們知道最耗時的服務是什麼。下圖是Zipkin的服務調用時間分佈。
服務的TOP N視圖。所謂TOP N視圖就是一個系統請求的排名狀況。通常來講,這個排名會有三種排名的方法:a)按調用量排名,b) 按請求最耗時排名,c)按熱點排名(一個時間段內的請求次數的響應時間和)。
數據庫操做關聯。對於Java應用,咱們能夠很方便地經過JavaAgent字節碼注入技術拿到JDBC執行數據庫操做的執行時間。對此,咱們能夠和相關的請求對應起來。
這樣一來,咱們就能夠知道服務和基礎層資源的關係。若是是Java應用,咱們還要和JVM裏的東西進行關聯,這樣咱們才能知道服務所運行的JVM中的狀況(好比GC的狀況)。
有了這些數據上的關聯,咱們就能夠達到以下的目標。
當一臺機器掛掉是由於CPU或I/O太高的時候,咱們立刻能夠知道其會影響到哪些對外服務的API。
當一個服務響應過慢的時候,咱們立刻能關聯出來是否在作Java GC,或是其所在的計算結點上是否有資源不足的狀況,或是依賴的服務是否出現了問題。
當發現一個SQL操做過慢的時候,咱們能立刻知道其會影響哪一個對外服務的API。
當發現一個消息隊列擁塞的時候,咱們能立刻知道其會影響哪些對外服務的API。
總之,咱們就是想知道用戶訪問哪些請求會出現問題,這對於咱們瞭解故障的影響面很是有幫助。
一旦瞭解了這些信息,咱們就能夠作出調度。好比:
一旦發現某個服務過慢是由於CPU使用過多,咱們就能夠作彈性伸縮。
一旦發現某個服務過慢是由於MySQL出現了一個慢查詢,咱們就沒法在應用層上作彈性伸縮,只能作流量限制,或是降級操做了。
因此,一個分佈式系統,或是一個自動化運維繫統,或是一個Cloud Native的雲化系統,最重要的事就是把監控系統作好。在把數據收集好的同時,更重要的是把數據關聯好。這樣,咱們纔可能很快地定位故障,進而才能進行自動化調度。
上圖只是簡單地展現了一個分佈式系統的服務調用連接上都在報錯,其根本緣由是數據庫連接過多,服務不過來。另一個緣由是,Java在作Full GC致使處理過慢。因而,消息隊列出現消息堆積堵塞。這個圖只是一個示例,其形象地體現了在分佈式系統中監控數據關聯的重要性。
回顧一下今天的要點內容。首先,我強調了全棧系統監控的重要性,它就像是咱們的眼睛,沒有它,咱們根本就不知道系統到底發生了什麼。隨後,從基礎層、中間層和應用層三個層面,講述了全棧監控系統要監控哪些內容。而後,闡釋了什麼纔是好的監控系統,以及如何作出好的監控。最後,歡迎你分享一下你在監控系統中的比較好的實踐和方法。
服務治理,你應該聽得不少了。可是我想說,你所聽到的服務治理可能混合了流量調度等其它內容。咱們這裏會把服務治理和流量調度分開來說。因此,這裏只涉及服務治理上的一些關鍵技術,主要有如下幾點。
下面,咱們先看看服務關鍵程度和服務的依賴關係。關於服務關鍵程度,主要是要咱們梳理和定義服務的重要程度。這不是使用技術能夠完成的,這須要細緻地管理對業務的理解,才能定義出架構中各個服務的重要程度。
而後,咱們還要梳理出服務間的依賴關係,這點也很是重要。咱們常說,「沒有依賴,就沒有傷害」。這句話的意思就是說,服務間的依賴是一件很易碎的事。依賴越多,依賴越複雜,咱們的系統就越易碎。
由於依賴關係就像「鐵鎖連環」同樣,一個服務的問題很容易出現一條鏈上的問題。所以,傳統的SOA但願經過ESB來解決服務間的依賴關係,這也是爲何微服務中但願服務間是沒有依賴的,而讓上層或是前端業務來整合這些個後臺服務。
可是要真正作到服務無依賴,我認爲仍是比較有困難的,老是會有一些公有服務會被依賴。咱們只能是下降服務依賴的深度和廣度,從而讓管理更爲簡單和簡潔。在這一點上,以Spring boot爲首的微服務開發框架給開了一個好頭。
微服務是服務依賴最優解的上限,而服務依賴的下限是千萬不要有依賴環。若是系統架構中有服務依賴環,那麼代表你的架構設計是錯誤的。循環依賴有不少的反作用,最大的問題是這是一種極強的耦合,會致使服務部署至關複雜和難解,並且會致使無窮盡的遞歸故障和一些你意想不到的的問題。
解決服務依賴環的方案通常是,依賴倒置的設計模式。在分佈式架構上,你能夠使用一個第三方的服務來解決這個事。好比,經過訂閱或發佈消息到一個消息中間件,或是把其中的依賴關係抽到一個第三方的服務中,而後由這個第三方的服務來調用這些本來循環依賴的服務。
服務的依賴關係是能夠經過技術的手段來發現的,這其中,Zipkin是一個很不錯的服務調用跟蹤系統,它是經過 Google Dapper這篇論文來實現的。這個工具能夠幫你梳理服務的依賴關係,以及瞭解各個服務的性能。
在梳理完服務的重要程度和服務依賴關係以後,咱們就至關於知道了整個架構的全局。就好像咱們獲得了一張城市地圖,在這張地圖上能夠看到城市的關鍵設施,以及城市的主幹道。再加上相關的監控,咱們就能夠看到城市各條道路上的工做和擁堵狀況。這對於咱們整個分佈式架構是很是很是關鍵的。
我給不少公司作過相關的諮詢。當他們須要我幫忙解決一些高併發或是架構問題的時候,我通常都會向他們要一張這樣的「地圖」,可是幾乎全部的公司都沒有這樣的地圖。
有了上面這張地圖後,咱們還須要有一個服務發現的中間件,這個中間件是很是很是關鍵的。由於這個「架構城市」是很是動態的,有的服務會新加進來,有的會離開,有的會增長更多的實例,有的會減小,有的服務在維護過程當中(發佈、伸縮等),因此咱們須要有一個服務註冊中心,來知道這麼幾個事。
這個服務註冊中心有點像咱們系統運維同窗說的CMDB這樣的東西,它也是很是之關鍵的,由於沒有這個東西,咱們將沒法知道這些服務運做的狀態和狀況。
有了這些服務的狀態和運行狀況以後,你就須要對這些服務的生命週期進行管理了。服務的生命週期一般會有如下幾個狀態:
這幾個狀態須要管理好,否則的話,你將不知道這些服務在什麼樣的狀態下。不知道在什麼樣的狀態下,你對整個分佈式架構也就沒法控制了。
有了這些服務的狀態和生命週期的管理,以及服務的重要程度和服務的依賴關係,再加上一個服務運行狀態的擬合控制(後面會提到),你一會兒就有了管理整個分佈式服務的手段了。一個紛亂無比的世界就能夠乾乾淨淨地管理起來了。
對於整個架構的版本管理這個事,我只見到亞馬遜有這個東西,叫VersionSet,也就是由一堆服務的版本集所造成的整個架構的版本控制。
除了各個項目的版本管理以外,還須要在上面再蓋一層版本管理。若是Build過Linux分發包,那麼你就會知道,Linux分發包中各個軟件的版本上會再蓋一層版本控制。畢竟,這些分發包也是有版本依賴的,這樣能夠解決各個包的版本兼容性問題。
因此,在分佈式架構中,咱們也須要一個架構的版本,用來控制其中各個服務的版本兼容。好比,A服務的1.2版本只能和B服務的2.2版本一塊兒工做,A服務的上個版本1.1只能和B服務的2.0一塊兒工做。這就是版本兼容性。
若是架構中有這樣的問題,那麼咱們就須要一個上層架構的版本管理。這樣,若是咱們要回滾一個服務的版本,就能夠把與之有版本依賴的服務也一塊兒回滾掉。
固然,通常來講,在設計過程當中,咱們但願沒有版本的依賴性問題。但可能有些時候,咱們會有這樣的問題,那麼就須要在架構版本中記錄下這個事,以即可以回滾到上一次相互兼容的版本。
要作到這個事,你須要一個架構的manifest,一個服務清單,這個服務清單定義了全部服務的版本運行環境,其中包括但不限於:
每一次對這個清單的變動都須要被記錄下來,算是一個架構的版本管理。而咱們上面所說的那個集羣控制系統須要可以解讀並執行這個清單中的變動,以操做和管理整個集羣中的相關變動。
服務和資源的調度有點像操做系統。操做系統一方面把用戶進程在硬件資源上進行調度,另外一方面提供進程間的通訊方式,可讓不一樣的進程在一塊兒協同工做。服務和資源調度的過程,與操做系統調度進程的方式很類似,主要有如下一些關鍵技術。
所謂服務狀態不是服務中的數據狀態,而是服務的運行狀態。也就是服務的Status,而不是State。也就是上述服務運行時生命週期中的狀態——Provision,Ready,Run,Scale,Rollback,Update,Destroy,Failed……
服務運行時的狀態是很是關鍵的。服務運行過程當中,狀態也是會有變化的,這樣的變化有兩種。
一種是不預期的變化。好比,服務運行由於故障致使一些服務掛掉,或是別的什麼緣由出現了服務不健康的狀態。而一個好的集羣管理控制器應該可以強行維護服務的狀態。在健康的實例數變少時,控制器會把不健康的服務給摘除,而又啓動幾個新的,強行維護健康的服務實例數。
另一種是預期的變化。好比,咱們須要發佈新版本,須要伸縮,須要回滾。這時,集羣管理控制器就應該把集羣從現有狀態遷移到另外一個新的狀態。這個過程並非一蹴而就的,集羣控制器須要一步一步地向集羣發送若干控制命令。這個過程叫「擬合」——從一個狀態擬合到另外一個狀態,並且要窮盡全部的可能,玩命地不斷地擬合,直到達到目的。
詳細說明一下,對於分佈式系統的服務管理來講,當須要把一個狀態變成另外一個狀態時,咱們須要對集羣進行一系列的操做。好比,當須要對集羣進行Scale的時候,咱們須要:
能夠看到,這是一個比較穩健和嚴謹的Scale過程,這須要集羣控制器往生產集羣中進行若干次操做。
這個操做的過程必定是比較「慢」的。一方面,須要對其它操做排它;另外一方面,在整個過程當中,咱們的控制系統須要努力地逼近最終狀態,直到徹底達到。此外,正在運行的服務可能也會出現問題,離開了咱們想要的狀態,而控制系統檢測到後,會強行地維持服務的狀態。
咱們把這個過程就叫作「擬合」。基本上來講,集羣控制系統都是要幹這個事的。沒有這種設計的控制系統都不能算作設計精良的控制系統,並且在運行時必定會有不少的坑和bug。
若是研究過Kubernetes這個調度控制系統,你就會看到它的思路就是這個樣子的。
有了上述的服務狀態擬合的基礎工做以後,咱們就能很容易地管理服務的生命週期了,甚至能夠經過底層的支持進行便利的服務彈性伸縮和故障遷移。
對於彈性伸縮,在上面我已經給出了一個服務伸縮所須要的操做步驟。仍是比較複雜的,其中涉及到了:
而對於故障遷移,也就是服務的某個實例出現問題時,咱們須要自動地恢復它。對於服務來講,有兩種模式,一種是寵物模式,一種是奶牛模式。
對於這兩種模式,在運行中也是比較複雜的,其中涉及到了:
咱們能夠看到,彈性伸縮和故障恢復須要很類似的技術步驟。可是,要完成這些事情並不容易,你須要作不少工做,並且有不少細節上的問題會讓你感到焦頭爛額。
固然,好消息是,咱們很是幸運地生活在了一個比較不錯的時代,由於有Docker和Kubernetes這樣的技術,能夠很是容易地讓咱們作這個工做。
可是,須要把傳統的服務遷移到Docker和Kubernetes上來,再加上更上層的對服務生命週期的控制系統的調度,咱們就能夠作到一個徹底自動化的運維架構了。
正如上面和操做系統作的類比同樣,一個好的操做系統須要可以經過必定的機制把一堆獨立工做的進程給協同起來。在分佈式的服務調度中,這個工做叫作Orchestration,國內把這個詞翻譯成「編排」。
從《分佈式系統架構的冰與火》一文中的SOA架構演化圖來看,要完成這個編排工做,傳統的SOA是經過ESB(Enterprise Service Bus)——企業服務總線來完成的。ESB的主要功能是服務通訊路由、協議轉換、服務編制和業務規則應用等。
注意,ESB的服務編制叫Choreography,與咱們說的Orchestration是不同的。
Orchestration的意思是,一個服務像大腦同樣來告訴你們應該怎麼交互,就跟樂隊的指揮同樣。(查看Service-oriented Design:A Multi-viewpoint Approach,瞭解更多信息)。
Choreography的意思是,在各自完成專屬本身的工做的基礎上,怎樣互相協做,就跟芭蕾舞團的舞者同樣。
而在微服務中,咱們但願使用更爲輕量的中間件來取代ESB的服務編排功能。
簡單來講,這須要一個API Gateway或一個簡單的消息隊列來作相應的編排工做。在Spring Cloud中,全部的請求都統一經過API Gateway(Zuul)來訪問內部的服務。這個和Kubernetes中的Ingress類似。
我以爲,關於服務的編排會直接致使一個服務編排的工做流引擎中間件的產生,這多是由於我受到了亞馬遜的軟件工程文化的影響所致——亞馬遜是一家超級喜歡工做流引擎的公司。經過工做流引擎,能夠很是快速地將若干個服務編排起來造成一個業務流程。(你能夠看一下AWS上的Simple Workflow服務。)
這就是所謂的Orchestration中的conductor 指揮了。
好了,今天的分享就這些。總結一下今天的主要內容:咱們從服務關鍵程度、服務依賴關係、整個架構的版本管理等多個方面,全面闡述了分佈式系統架構五大關鍵技術之一——服務資源調度。但願這些內容能對你有所啓發。
關於流量調度,如今不少架構師都把這個事和服務治理混爲一談了。我以爲仍是應該分開的。一方面,服務治理是內部系統的事,而流量調度能夠是內部的,更是外部接入層的事。另外一方面,服務治理是數據中心的事,而流量調度要作得好,應該是數據中心以外的事,也就是咱們常說的邊緣計算,是應該在相似於CDN上完成的事。
因此,流量調度和服務治理是在不一樣層面上的,不該該混在一塊兒,因此在系統架構上應該把它們分開。
對於一個流量調度系統來講,其應該具備的主要功能是:
依據系統運行的狀況,自動地進行流量調度,在無需人工干預的狀況下,提高整個系統的穩定性;
讓系統應對爆品等突發事件時,在彈性計算擴縮容的較長時間窗口內或底層資源消耗殆盡的狀況下,保護系統平穩運行。
這仍是爲了提升系統架構的穩定性和高可用性。
此外,這個流量調度系統還能夠完成如下幾方面的事情。
全部的這些都應該是一個API Gateway應該作的事。
可是,做爲一個API Gateway來講,由於要調度流量,首先須要扛住流量,並且還須要有一些比較輕量的業務邏輯,因此一個好的API Gateway須要具有如下的關鍵技術。
高性能。API Gateway必須使用高性能的技術,因此,也就須要使用高性能的語言。
扛流量。要能扛流量,就須要使用集羣技術。集羣技術的關鍵點是在集羣內的各個結點中共享數據。這就須要使用像Paxos、Raft、Gossip這樣的通信協議。由於Gateway須要部署在廣域網上,因此還須要集羣的分組技術。
業務邏輯。API Gateway須要有簡單的業務邏輯,因此,最好是像AWS的Lambda 服務同樣,可讓人注入不一樣語言的簡單業務邏輯。
服務化。一個好的API Gateway須要可以經過Admin API來不停機地管理配置變動的,而不是經過一個.conf文件來人肉地修改配置。
基於上述的這幾個技術要求,就其本質來講,目前能夠作成這樣的API Gateway幾乎沒有。這也是爲何我如今本身開發一個的緣由。你能夠到個人官網MegaEase.com上查看相關的產品和技術信息。
對於服務調度來講,最難辦的就是有狀態的服務了。這裏的狀態是State,也就是說,有些服務會保存一些數據,而這些數據是不能丟失的,因此,這些數據是須要隨服務一塊兒調度的。
通常來講,咱們會經過「轉移問題」的方法來讓服務變成「無狀態的服務」。也就是說,會把這些有狀態的東西存儲到第三方服務上,好比Redis、MySQL、ZooKeeper,或是NFS、Ceph的文件系統中。
這些「轉移問題」的方式把問題轉移到了第三方服務上,因而本身的Java或PHP服務中沒有狀態,可是Redis和MySQL上則有了狀態。因此,咱們能夠看到,如今的分佈式系統架構中出問題的基本都是這些存儲狀態的服務。
由於數據存儲結點在Scale上比較困難,因此成了一個單點的瓶頸。
要解決數據結點的Scale問題,也就是讓數據服務能夠像無狀態的服務同樣在不一樣的機器上進行調度,就會涉及數據的replication問題。而數據replication則會帶來數據一致性的問題,進而對性能帶來嚴重的影響。
要解決數據不丟的問題,只能經過數據冗餘的方法,就算是數據分區,每一個區也須要進行數據冗餘處理。這就是數據副本。當出現某個節點的數據丟失時,能夠從副本讀到。數據副本是分佈式系統解決數據丟失異常的惟一手段。簡單來講:
在解決數據副本間的一致性問題時,咱們有一些技術方案。
你能夠仔細地讀一下我在3年前寫的《分佈式系統的事務處理》這篇文章。其中我引用了Google App Engine聯合創始人賴安·巴里特(Ryan Barrett)在2009年Google I/O上的演講Transaction Across DataCenter視頻 中的一張圖。
從上面這張經典的圖中,咱們能夠看到各類不一樣方案的對比。
如今,不少公司的分佈式系統事務基本上都是兩階段提交的變種。好比:阿里推出的TCC–Try–Confirm–Cancel,或是我在亞馬遜見到的Plan–Reserve–Confirm的方式,等等。凡是經過業務補償,或是在業務應用層上作的分佈式事務的玩法,基本上都是兩階段提交,或是兩階段提交的變種。
換句話說,迄今爲止,在應用層上解決事務問題,只有「兩階段提交」這樣的方式,而在數據層解決事務問題,Paxos算法則是不二之選。
真正完整解決數據Scale問題的應該仍是數據結點自身。只有數據結點自身解決了這個問題,才能作到對上層業務層的透明,業務層能夠像操做單機數據庫同樣來操做分佈式數據庫,這樣才能作到整個分佈式服務架構的調度。
也就是說,這個問題應該解決在數據存儲方。可是由於數據存儲結果有太多不一樣的Scheme,因此如今的數據存儲也是多種多樣的,有文件系統,有對象型的,有Key-Value式,有時序的,有搜索型的,有關係型的……
這就是爲何分佈式數據存儲系統比較難作,由於很難作出來一個放之四海皆準的方案。類比一下編程中的各類不一樣的數據結構你就會明白爲何會有這麼多的數據存儲方案了。
可是咱們能夠看到,這個「數據存儲的動物園」中,基本上都在解決數據副本、數據一致性和分佈式事務的問題。
好比:AWS的Aurora,就是改寫了MySQL的InnoDB引擎。爲了承諾高可用的SLA,須要寫6個副本。其不像國內的MySQL的經過bin log的數據複製,而是更爲「驚豔」地複製SQL語句,而後拼命地使用各類tricky的方式來下降latency。好比,使用多線程並行、使用SQL操做的merge等。
MySQL官方也有MySQL Cluster的技術方案。此外,MongoDB、國內的PingCAP的TiDB、國外的CockroachDB,還有阿里的OceanBase都是爲了解決大規模數據的寫入和讀取的問題而出現的數據庫軟件。因此,我以爲成熟的能夠用到生產線上的分佈式數據庫這個事估計也不遠了。
而對於一些須要文件存儲的,則須要分佈式文件系統的支持。試想,一個Kafka或ZooKeeper須要把它們的數據存儲到文件系統上。當這個結點有問題時,咱們須要再啓動一個Kafka或ZooKeeper的實例,那麼也須要把它們持久化的數據搬遷到另外一臺機器上。
(注意,雖然Kafka和ZooKeeper是HA的,數據會在不一樣的結點中進行復制,可是咱們也應該搬遷數據,這樣有利用於新結點的快速啓動。不然,新的結點須要等待數據同步,這個時間會比較長,可能會致使數據層的其它問題。)
因而,咱們就須要一個底層是分佈式的文件系統,這樣新的結點只須要作一個簡單的遠程文件系統的mount就能夠把數據調度到另一臺機器上了。
因此,真正解決數據結點調度的方案應該是底層的數據結點。在它們上面作這個事纔是真正有效和優雅的。而像阿里的用於分庫分表的數據庫中間件TDDL或是別的公司叫什麼DAL 之類的這樣的中間件都會成爲過渡技術。
咱們對狀態數據調度作個小小的總結。
對於應用層上的分佈式事務一致性,只有兩階段提交這樣的方式。
而底層存儲能夠解決這個問題的方式是經過一些像Paxos、Raft或是NWR這樣的算法和模型來解決。
狀態數據調度應該是由分佈式存儲系統來解決的,這樣會更爲完美。可是由於數據存儲的Scheme太多,因此,致使咱們有各式各樣的分佈式存儲系統,有文件對象的,有關係型數據庫的,有NoSQL的,有時序數據的,有搜索數據的,有隊列的……
總之,我相信狀態數據調度應該是在IaaS層的數據存儲解決的問題,而不是在PaaS層或者SaaS層來解決的。
在IaaS層上解決這個問題,通常來講有三種方案,一種是使用比較廉價的開源產品,如:NFS、Ceph、TiDB、CockroachDB、ElasticSearch、InfluxDB、MySQL Cluster和Redis Cluster之類的;另外一種是用雲計算廠商的方案。固然,若是不差錢的話,能夠使用更爲昂貴的商業網絡存儲方案。
回顧一下今天分享的主要內容。首先,我先明確表態,不要將流量調度和服務治理混爲一談(固然,服務治理是流量調度的前提),並比較了二者有何不一樣。而後,講述了流量調度的主要功能和關鍵技術。接着進入本文的第二個話題——狀態數據調度,講述了真正完整解決數據Scale問題的應該仍是數據結點自身,並給出了相應的技術方案,隨後對狀態數據調度進行了小結。
歡迎你也談一談經歷過的技術場景中是採用了哪些流量和數據調度的技術和產品,遇到過什麼樣的問題,是怎樣解決的?
下篇文章中,咱們將開啓一個全新的話題——洞悉PaaS平臺的本質。
在瞭解了前面幾篇文章中提的這些問題之後,咱們須要思考一下該怎樣解決這些問題。爲了解決這些問題,請先讓我來談談軟件工程的本質。
我認爲,一家商業公司的軟件工程能力主要體如今三個地方。
第一,提升服務的SLA。
所謂服務的SLA,也就是咱們能提供多少個9的系統可用性,而每提升一個9的可用性都是對整個系統架構的從新洗禮。而提升系統的SLA主要表如今兩個方面:
你能夠看一下我在CoolShell上寫的《關於高可用系統》,這篇文章主要講了構建高可用的系統須要使用分佈式系統設計思路。然而這還不夠,還須要一個高度自動化的運維和管理系統,由於故障是常態,若是沒有自動化的故障恢復,很難提升服務的SLA。
第二,能力和資源重用或複用。
軟件工程還有一個重要的能力是讓能力和資源能夠重用。其主要表如今以下兩個方面:
爲此,須要咱們有兩個重要的能力:一個是「軟件抽象的能力」,另外一個是「軟件標準化的能力」。你能夠認爲軟件抽象就是找出通用的軟件模塊或服務,軟件標準化就是使用統一的軟件通信協議、統一的開發和運維管理方法……這樣能讓總體軟件開發運維的能力和資源獲得最大程度的複用,從而增長效率。
第三,過程的自動化。
編程原本就是把一個重複的工做自動化的過程,因此,軟件工程的第三個本質就是把軟件生產和運維的過程自動化起來。也就是下面這兩個方面:
爲此,咱們除了須要CI/CD的DevOps式的自動化,也須要可以對正在運行的生產環境中的軟件進行自動化運維。
經過了解軟件工程的這三個本質,你會發現,咱們上面所說的那些分佈式的技術點是高度一致的,也就是下面這三個方面的能力。(是的,世界就是這樣的。當參透了本質以後,你會發現世界是大同的。)
只有作到了這些,咱們纔可以真正擁有云計算的威力。這就是所謂的Cloud Native。而這些目標都完美地體如今PaaS平臺上。
前面講述的分佈式系統關鍵技術和軟件工程的本質,均可以在PaaS平臺上獲得徹底體現。因此,須要一個PaaS平臺把那麼多的東西給串聯起來。這裏,我結合本身的認知給你講一下PaaS相關的東西,並把前面講過的全部東西作一個總結。
一個好的PaaS平臺應該具備分佈式、服務化、自動化部署、高可用、敏捷以及分層開放的特徵,並可與IaaS實現良好的聯動。
下面這三件事是PaaS跟傳統中間件最大的差異。
從下面的圖中能夠看到,我用了Docker+Kubernetes層來作了一個「技術緩衝層」。也就是說,若是沒有Docker和Kubernetes,構建PaaS將會複雜不少。固然,若是你正在開發一個相似PaaS的平臺,那麼你會發現本身開發出來的東西會跟Docker和Kubernetes很是像。相信我,最終你仍是會放棄本身的輪子而採用Docker+Kubernetes的。
在Docker+Kubernetes層之上,咱們看到了兩個相關的PaaS層。一個是PaaS調度層,不少人將其稱爲iPaaS;另外一個是PaaS能力層,一般被稱爲aPaaS。沒有PaaS調度層,PaaS能力層很難被管理和運維,而沒有PaaS能力層,PaaS就失去了提供實際能力的業務價值。而本文更多的是在講PaaS調度層上的東西。
在兩個相關的PaaS層之上,有一個流量調度的接入模塊,這也是PaaS中很是關鍵的東西。流控、路由、降級、灰度、聚合、串聯等等都在這裏,包括最新的AWS Lambda Service的小函數等也能夠放在這裏。這個模塊應該是像CDN那樣來部署的。
而後,在這個圖的兩邊分別是與運營和運維相關的。運營這邊主要是管理一些軟件資源方面的東西(像DockerHub和CMDB的東西),以及外部接入和開放平臺上的東西,這主要是對外提供能力的相關組件;而運維這邊主要是對內的相關東西,主要就是DevOps的這套東西。
總結一下,一個完整的PaaS平臺會包括如下幾部分。
由於我畫的是一個大而全的東西,因此看上去彷佛很重很複雜。實際上,其中的不少東西是能夠根據本身的需求被簡化和裁剪的,並且不少開源軟件能幫你簡化好多工做。雖然構建PaaS平臺看上去很麻煩,可是其實並非很複雜,不要被我嚇到了。哈哈。
下面的圖給出了一個大概的軟件生產、運維和服務接入,它把以前的東西都串起來了。
從左上開始軟件構建,進入軟件資產庫(Docker Registry+一些軟件的定義),而後走DevOps的流程,經過總體架構控制器進入生產環境,生產環境經過控制器操做Docker+Kubernetes集羣進行軟件部署和生產變動。
其中,同步服務的運行狀態,並經過生命週期管理來擬合狀態,如圖右側部分所示。服務運行時的數據會進入到相關應用監控,應用監控中的一些監控事件會同步到生命週期管理中,再由生命週期管理器來作出決定,經過控制器來調度服務運行。當應用監控中心發現流量變化,要進行強制性伸縮時,它經過生命週期管理來通知控制系統進行伸縮。
左下是服務接入的相關組件,主要是網關服務,以及API聚合編排和流程處理。這對應於以前說過的流量調度和API Gateway的相關功能。
恭喜你,已經聽完了《分佈式系統架構的本質》系列文章的7篇文章。下面,咱們對這些內容作一下總結。
傳統的單體架構系統容量顯然是有上限的。同時,爲了應對有計劃和無計劃的下線時間,系統的可用性也是有其極限的。分佈式系統爲以上兩個問題提供瞭解決方案,而且還附帶有其餘優點。可是,要同時解決這兩個問題決非易事。爲了構建分佈式系統,咱們面臨的主要問題以下。
爲了解決這些問題,咱們深刻了解了如下這些解決方案。
你已經看到,解決分佈式服務的吞吐量和可用性問題不是件容易的事,以及目前的主流技術是怎麼辦到的。衍生出來的許多子問題,每個都值得去細化、去研究其解決方案。這已經超出本文的篇幅所能及的了,但的確都是值得咱們作技術的人去深刻思考的。
咱們在以前的系列文章《分佈式系統架構的本質》中說過,分佈式系統的一個關鍵技術是「數據調度」。由於咱們須要擴充節點,提升系統的高可用性,因此必需冗餘數據結點。創建數據結點的副本看上去容易,但其中最大的難點就是分佈式一致性的問題。下面,我會帶你看看數據調度世界中的一些技術點以及相關的技術論文。
對於分佈式的一致性問題,相信你在前面看過好幾回下面這張圖。從中,咱們能夠看出,Paxos算法的重要程度。還有人說,分佈式下真正的一致性算法只有Paxos算法。
Paxos算法,是萊斯利·蘭伯特(Lesile Lamport)於1990年提出來的一種基於消息傳遞且具備高度容錯特性的一致性算法。可是這個算法太過於晦澀,因此,一直以來都處於理論上的論文性質的東西。
其進入工程圈的源頭在於Google的Chubby lock——一個分佈式的鎖服務,用在了Bigtable中。直到Google發佈了下面的這兩篇論文,Paxos才進入到工程界的視野中來。
Google與Big Table相齊名的還有另外兩篇論文。
不過,這幾篇論文中並無講太多的Paxos算法細節上的內容,反而在論文Paxos Made Live – An Engineering Perspective 中提到了不少工程實現的細節。好比,Google實現Paxos時遇到的各類問題和解決方案,講述了從理論到實際應用兩者之間巨大的鴻溝。
尤爲在滿地都是坑的分佈式系統領域,這篇論文沒有過多討論Paxos算法自己,而是在討論如何將理論應用到實踐,如何彌補理論在實踐中的不足,如何取捨,如何測試,這些在實踐中的各類問題纔是工程的魅力。因此建議你讀一讀。
Paxos算法的原版論文我在這裏就不貼了,由於一來比較晦澀,二來也不易懂。推薦一篇比較容易讀的——Neat Algorithms - Paxos ,這篇文章中還有一些小動畫幫助你讀懂。還有一篇能夠幫你理解的文章是Paxos by Examples。
若是你要本身實現Paxos算法,這裏有幾篇文章供你參考。
Paxos Made Code ,做者是馬克羅·普里米(Macro Primi),他實現了一個Paxos開源庫libpaxos。
Paxos for System Builders ,以一個系統實現者的角度討論了實現Paxos的諸多具體問題,好比Leader選舉、數據及消息類型、流控等。
Paxos Made Moderately Complex,這篇文章比較新,是2011年才發表的。文中介紹了不少實現細節,並提供了不少僞代碼,一方面能夠幫助理解Paxos,另外一方面也能夠據此實現一個Paxos。
Paxos Made Practical主要介紹如何採用Paxos實現replication。
除了馬克羅·普里米的那個開源實現外,到GitHub上找一下,你就會看到這些項目:Plain Paxos Implementations Python & Java、A go implementation of the Paxos algorithm 。
ZooKeeper 有和Paxos很是類似的一些特徵,好比,領導選舉、提案號等,可是它本質上不是Paxos協議,而是本身發明的Zab協議,有興趣的話,能夠讀一下這篇論文:Zab: High-Performance broadcast for primary-backup systems。
上述的Google File System、MapReduce、Bigtable並稱爲「谷三篇」。基本上來講,整個世界工程系統由於這三篇文章,開始向分佈式系統演化,而云計算中的不少關鍵技術也是由於這三篇文章才得以成熟。 後來,雅虎公司也基於這三篇論文開發了一個開源的軟件——Hadoop。
由於Paxos算法太過於晦澀,並且在實際的實現上有太多的坑,並不太容易寫對。因此,有人搞出了另一個一致性的算法,叫Raft。其原始論文是 In search of an Understandable Consensus Algorithm (Extended Version) 尋找一種易於理解的Raft算法。這篇論文的譯文在InfoQ上《Raft一致性算法論文譯文》,推薦你讀一讀。
Raft算法和Paxos的性能和功能是同樣的,可是它和Paxos算法的結構不同,這使Raft算法更容易理解而且更容易實現。那麼Raft是怎樣作到的呢?
Raft把這個一致性的算法分解成了幾個部分,一個是領導選舉(Leader Selection),一個是日誌複製(Log Replication),一個是安全性(Safety),還有一個是成員變化(Membership Changes)。對於通常人來講,Raft協議比Paxos的學習曲線更低,也更平滑。
Raft協議中有一個狀態機,每一個結點會有三個狀態,分別是 Leader、Candidate和Follower。Follower只響應其餘服務器的請求,若是沒有收到任何信息,它就會成爲一個Candidate,並開始進行選舉。收到大多數人贊成選票的人會成爲新的Leader。
一旦選舉出了一個Leader,它就開始負責服務客戶端的請求。每一個客戶端的請求都包含一個要被複制狀態機執行的指令。Leader首先要把這個指令追加到log中造成一個新的entry,而後經過AppendEntries RPC並行地把該entry發給其餘服務器(server)。若是其餘服務器沒發現問題,複製成功後會給Leader一個表示成功的ACK。
Leader收到大多數ACK後應用該日誌,返回客戶端執行結果。若是Follower崩潰 (crash)或者丟包,Leader會不斷重試AppendEntries RPC。
這裏推薦幾個不錯的Raft算法的動畫演示。
後面,業內又搞出來一些工程上的東西,好比Amazon的DynamoDB,其論文Dynamo: Amazon's Highly Available Key Value Store 的影響力很是大。這篇論文中講述了Amazon 的DynamoDB是如何知足系統的高可用、高擴展和高可靠的。其中展現了系統架構是如何作到數據分佈以及數據一致性的。
GFS採用的是查表式的數據分佈,而DynamoDB採用的是計算式的,也是一個改進版的經過虛擬結點減小增長結點帶來數據遷移的一致性哈希。另外,這篇論文中還講述了一個NRW模式用於讓用戶能夠靈活地在CAP系統中選取其中兩項,這使用到了Vector Clock——向量時鐘來檢測相應的數據衝突。最後還介紹了使用Handoff的機制對可用性的提高。
這篇文章中有幾個關鍵的概念,一個是Vector Clock,另外一個是Gossip協議。
提到向量時鐘就須要提一下邏輯時鐘。所謂邏輯時間,也就是在分佈系統中爲了解決消息有序的問題,因爲在不一樣的機器上有不一樣的本地時間,這些本地時間的同步很難搞,會致使消息亂序。
因而Paxos算法的發明人蘭伯特(Lamport)搞了個向量時鐘,每一個系統維護一個本地的計數器,這就是所謂的邏輯時鐘。每執行一個事件(例如向網絡發送消息,或是交付到應用層)都對這個計數器作加1操做。當跨系統的時候,在消息體上附着本地計算器,當接收端收到消息時,更新本身的計數器(取對端傳來的計數器和本身當成計數器的最大值),也就是調整本身的時鐘。
邏輯時鐘能夠保證,若是事件A先於事件B,那麼事件A的時鐘必定小於事件B的時鐘,可是返過來則沒法保證,由於返過來沒有因果關係。因此,向量時鐘解釋了因果關係。向量時鐘維護了數據更新的一組版本號(版本號其實就是使用邏輯時鐘)。
假如一個數據須要存在三個結點上A、B、C。那麼向量維度就是3,在初始化的時候,全部結點對於這個數據的向量版本是[A:0, B:0, C:0]。當有數據更新時,好比從A結點更新,那麼,數據的向量版本變成[A:1, B:0, C:0],而後向其餘結點複製這個版本,其在語義上表示爲我當前的數據是由A結果更新的,而在邏輯上則可讓分佈式系統中的數據更新的順序找到相關的因果關係。
這其中的邏輯關係,你能夠看一下 馬薩諸塞大學課程 Distributed Operating System 中第10節 Clock Synchronization 這篇講議。關於Vector Clock,你能夠看一下Why Vector Clocks are Easy和Why Vector Clocks are Hard 這兩篇文章。
另外,DynamoDB中使用到了Gossip協議來作數據同步,這個協議的原始論文是 Efficient Reconciliation and Flow Control for Anti-Entropy Protocols。Gossip算法也是Cassandra使用的數據複製協議。這個協議就像八卦和謠言傳播同樣,能夠 「一傳10、十傳百」傳播開來。可是這個協議看似簡單,細節上卻很是麻煩。
根據這篇論文,節點之間存在三種通訊方式。
push方式。A節點將數據(key,value,version)及對應的版本號推送給B節點,B節點更新A中比本身新的數據。
pull 方式。A僅將數據key,version推送給B,B將本地比A新的數據(key,value,version)推送給A,A更新本地。
push/pull方式。與pull相似,只是多了一步,A再將本地比B新的數據推送給B,B更新本地。
若是把兩個節點數據同步一次定義爲一個週期,那麼在一個週期內,push需通訊1次,pull需2次,push/pull則需3次。從效果上來說,push/pull最好,理論上一個週期內能夠使兩個節點徹底一致。直觀感受上,也是push/pull的收斂速度最快。
另外,每一個節點上的又須要一個協調機制,也就是如何交換數據能達到最快的一致性——消除節點的不一致性。上面所講的push、pull等是通訊方式,協調是在通訊方式下的數據交換機制。
協調所面臨的最大問題是,一方面須要找到一個經濟的方式,由於不可能每次都把一個節點上的數據發送給另外一個節點;另外一方面,還須要考慮到相關的容錯方式,也就是當由於網絡問題不可達的時候,怎麼辦?
通常來講,有兩種機制:一種是以固定機率傳播的Anti-Entropy機制,另外一種是僅傳播新到達數據的Rumor-Mongering機制。前者有完備的容錯性,可是須要更多的網絡和CPU資源,後者則反過來,不耗資源,但在容錯性上難以保證。
Anti-Entropy的機制又分爲Precise Reconciliation(精確協調)和Scuttlebutt Reconciliation(總體協調)這兩種。前者但願在每次通訊週期內都很是精確地消除雙方的不一致性,具體表現就是互發對方須要更新的數據。由於每一個結點均可以讀寫,因此這須要每一個數據都要獨立維護本身的版本號。
而總體協調與精確協調不一樣的是,總體協調不是爲每一個數據都維護單獨的版本號,而是每一個節點上的數據統一維護一個版本號,也就是一個一致的全局版本。這樣與其餘結果交換數據的時候,就只須要比較節點版本,而不是數據個體的版本,這樣會比較經濟一些。若是版本不同,則須要作精確協調。
由於篇幅問題,這裏就很少說了,你能夠看看原始的論文,還能夠去看看Cassandra中的源碼,以及到GitHub搜一下其餘人的實現。多說一句,Cassandra的實現是基於總體協調的push/pull模式。
關於Gossip的一些圖示化的東西,你能夠看一下動畫gossip visualization。
上面講的都是一些基本概念相關的東西,下面咱們來談談數據庫方面的一些論文。
一篇是AWS Aurora的論文 Amazon Aurora: Design Considerations for High Throughput Cloud –Native Relation Databases。
Aurora是AWS將MySQL的計算和存儲分離後,計算節點scale up,存儲節點scale out。並把其redo log獨立設計成一個存儲服務,把分佈式的數據方面的東西所有甩給了底層存儲系統。從而提升了總體的吞吐量和水平的擴展能力。
Aurora要寫6份拷貝,可是其只須要把一個Quorum中的日誌寫成功就能夠了。以下所示。能夠看到,將存儲服務作成一個跨數據中心的服務,提升數據庫容災,下降性能影響。
對於存儲服務的設計,核心的原理就是latency必定要低,畢竟寫6個copy是一件開銷很大的事。因此,基本上來講,Aurora用的是異步模型,而後拼命地作並行處理,其中用到的也是Gossip協議。以下所示。
在上面這個圖中,咱們能夠看到,完成前兩步,就能夠ACK回調用方。也就是說,只要數據在本地落地了,就能夠返回成功了。而後,對於六個副本,這個log會同時發送到6個存儲結點,只須要有大於4個成功ACK,就算寫成功了。第4步咱們能夠看到用的是Gossip協議。而後,第5步產生cache 頁,便於查詢。第6步在S3作Snapshot,相似於Checkpoint。
第二篇比較有表明的論文是Google的 Spanner: Google’s Globally-Distributed Database。 Spanner 是Google的全球分佈式數據庫Globally-Distributed Database) 。Spanner的擴展性達到了使人咋舌的全球級,能夠擴展到數百萬臺機器,數以百計的數據中心,上萬億的行。更給力的是,除了誇張的擴展性以外,它還能同時經過同步複製和多版原本知足外部一致性,可用性也是很好的。
下面是Spanserver的一個架構。
咱們能夠看到,每一個數據中心都會有一套Colossus,這是第二代的GFS。每一個機器有100-1000個tablet,也就是至關數據庫表中的行集,物理存儲就是數據文件。好比,一張表有2000行,而後有20個tablet,那麼每一個tablet分別有100行數據。
在tablet上層經過Paxos協議進行分佈式跨數據中心的一致性數據同步。Paxos會選出一個replica作Leader,這個Leader的壽命默認是10s,10s後重選。Leader就至關於複製數據的master,其餘replica的數據都是從它那裏複製的。讀請求能夠走任意的replica,可是寫請求只有去Leader。這些replica統稱爲一個Paxos Group。
Group之間也有數據交互傳輸,Google定義了最小傳輸複製單元directory,是一些有共同前綴的key記錄,這些key也有相同的replica配置屬性。
目前,基於Spanner論文的開源實現有兩個,一個是Google公司本身的人出來作的CockroachDB,另外一個是國人作的TiDB。
正如我在以前的分佈式系統的本質文章裏所說到的,分佈式的服務的調度須要一個分佈式的存儲系統來支持服務的數據調度。而咱們能夠看到,各大公司都在分佈式的數據庫上作各類各樣的創新,他們都在使用底層的分佈式文件系統來作存儲引擎,把存儲和計算分離開來,而後使用分佈式一致性的數據同步協議的算法來在上層提供高可用、高擴展的支持。
從這點來看,能夠預見到,過去的分庫分表並經過一個數據訪問的代理服務的玩法,應該在不久就會過期就會成爲歷史。真正的現代化的分佈式數據存儲就是Aurora和Spanner這樣的方式。
經過上面的這些論文和相關的工程實踐以及開源項目,相信可讓你在細節方面對分佈式中最難的一塊——數據調度方面有更多的認識。
前段時間,我寫了一系列分佈式系統架構方面的文章,有不少讀者紛紛留言討論相關的話題,還有讀者留言表示對分佈式系統架構這個主題感興趣,但願我能推薦一些學習資料。
就像我在前面的文章中屢次提到的,分佈式系統的技術棧巨大無比,因此我要推薦的學習資料也比較多,會在後面的文章中陸續發出。在今天這篇文章中,我將推薦一些分佈式系統的基礎理論和一些不錯的圖書和資料。
這篇文章比較長,因此我特地整理了目錄,幫你快速找到本身感興趣的內容。
下面這些基礎知識有可能你已經知道了,不過仍是容我把其分享在這裏。我但願用比較通俗易懂的文字將這些枯燥的理論知識講請楚。
CAP定理是分佈式系統設計中最基礎,也是最爲關鍵的理論。它指出,分佈式數據存儲不可能同時知足如下三個條件。
一致性(Consistency):每次讀取要麼得到最近寫入的數據,要麼得到一個錯誤。
可用性(Availability):每次請求都能得到一個(非錯誤)響應,但不保證返回的是最新寫入的數據。
分區容忍(Partition tolerance):儘管任意數量的消息被節點間的網絡丟失(或延遲),系統仍繼續運行。
也就是說,CAP定理代表,在存在網絡分區的狀況下,一致性和可用性必須二選一。而在沒有發生網絡故障時,即分佈式系統正常運行時,一致性和可用性是能夠同時被知足的。這裏須要注意的是,CAP定理中的一致性與ACID數據庫事務中的一致性大相徑庭。
掌握CAP定理,尤爲是可以正確理解C、A、P的含義,對於系統架構來講很是重要。由於對於分佈式系統來講,網絡故障在所不免,如何在出現網絡故障的時候,維持系統按照正常的行爲邏輯運行就顯得尤其重要。你能夠結合實際的業務場景和具體需求,來進行權衡。
例如,對於大多數互聯網應用來講(如門戶網站),由於機器數量龐大,部署節點分散,網絡故障是常態,可用性是必需要保證的,因此只有捨棄一致性來保證服務的AP。而對於銀行等,須要確保一致性的場景,一般會權衡CA和CP模型,CA模型網絡故障時徹底不可用,CP模型具有部分可用性。
CA (consistency + availability),這樣的系統關注一致性和可用性,它須要很是嚴格的全體一致的協議,好比「兩階段提交」(2PC)。CA系統不能容忍網絡錯誤或節點錯誤,一旦出現這樣的問題,整個系統就會拒絕寫請求,由於它並不知道對面的那個結點是否掛掉了,仍是隻是網絡問題。惟一安全的作法就是把本身變成只讀的。
CP (consistency + partition tolerance),這樣的系統關注一致性和分區容忍性。它關注的是系統裏大多數人的一致性協議,好比:Paxos算法(Quorum類的算法)。這樣的系統只須要保證大多數結點數據一致,而少數的結點會在沒有同步到最新版本的數據時變成不可用的狀態。這樣可以提供一部分的可用性。
AP (availability + partition tolerance),這樣的系統關心可用性和分區容忍性。所以,這樣的系統不能達成一致性,須要給出數據衝突,給出數據衝突就須要維護數據版本。Dynamo就是這樣的系統。
然而,仍是有一些人會錯誤地理解CAP定理,甚至誤用。Cloudera工程博客中,CAP Confusion: Problems with ‘partition tolerance’一文中對此有詳細的闡述。
在谷歌的Transaction Across DataCenter視頻中,咱們能夠看到下面這樣的圖。這個是CAP理論在具體工程中的體現。
本文是英文維基百科上的一篇文章。它是Sun公司的勞倫斯·彼得·多伊奇(Laurence Peter Deutsch)等人於1994~1997年提出的,講的是剛剛進入分佈式計算領域的程序員常會有的一系列錯誤假設。
多伊奇於1946年出生在美國波士頓。他創辦了阿拉丁企業(Aladdin Enterprises),並在該公司編寫出了著名的Ghostscript開源軟件,於1988年首次發佈。
他在學生時代就和艾倫·凱(Alan Kay)等比他年長的人一塊兒開發了Smalltalk,而且他的開發成果激發了後來Java語言JIT編譯技術的創造靈感。他後來在Sun公司工做併成爲Sun的公司院士。在1994年,他成爲了ACM院士。
基本上,每一個人剛開始創建一個分佈式系統時,都作了如下8條假定。隨着時間的推移,每一條都會被證實是錯誤的,也都會致使嚴重的問題,以及痛苦的學習體驗。
阿爾農·羅特姆-蓋爾-奧茲(Arnon Rotem-Gal-Oz)寫了一篇長文Fallacies of Distributed Computing Explained來解釋這些點。
因爲他寫這篇文章的時候已是2006年了,因此從中能看到這8條常見錯誤被提出十多年後還有什麼樣的影響:一是,爲何當今的分佈式軟件系統也須要避免這些設計錯誤;二是,在當今的軟硬件環境裏,這些錯誤意味着什麼。好比,文中在談「延遲爲零」假設時,還談到了AJAX,而這是2005年開始流行的技術。
而加勒思·威爾遜(Gareth Wilson)的文章則用平常生活中的例子,對這些點作了更爲通俗的解釋。
這8個須要避免的錯誤不只對於中間件和底層系統開發者及架構師是重要的知識,並且對於網絡應用程序開發者也一樣重要。分佈式系統的其餘部分,如容錯、備份、分片、微服務等也許能夠對應用程序開發者部分透明,但這8點則是應用程序開發者也必須知道的。
爲何咱們要深入地認識這8個錯誤?是由於,這要咱們清楚地認識到——在分佈式系統中錯誤是不可能避免的,咱們能作的不是避免錯誤,而是要把錯誤的處理當成功能寫在代碼中。
後面,我會寫一個系列的文章來談一談,分佈式系統容錯設計中的一些常見設計模式。敬請關注!
本文做者認爲,推薦大量的理論論文是學習分佈式系統理論的錯誤方法,除非這是你的博士課程。由於論文一般難度大又很複雜,須要認真學習,並且須要理解這些研究成果產生的時代背景,才能真正的領悟到其中的精妙之處。
在本文中,做者給出了他整理的分佈式工程師必需要掌握的知識列表,並直言掌握這些足夠設計出新的分佈式系統。首先,做者推薦了4份閱讀材料,它們共同歸納了構建分佈式系統的難點,以及全部工程師必須克服的技術難題。
Distributed Systems for Fun and Profit,這是一本小書,涵蓋了分佈式系統中的關鍵問題,包括時間的做用和不一樣的複製策略。後文中對這本書有較詳細的介紹。
Notes on distributed systems for young bloods,這篇文章中沒有理論,是一份適合新手閱讀的分佈式系統實踐筆記。
A Note on Distributed Systems,這是一篇經典的論文,講述了爲何在分佈式系統中,遠程交互不能像本地對象那樣進行。
The fallacies of distributed computing,每一個分佈式系統新手都會作的8個錯誤假設,並探討了其會帶來的影響。上文中專門對這篇文章作了介紹。
隨後,分享了幾個關鍵點。
能夠參考Lamport時鐘和Vector時鐘,還能夠看看Dynamo論文。
最終一致性以及其餘技術方案在以系統行爲弱保證爲代價,來試圖避免這種系統壓力。閱讀Dynamo論文和帕特·赫爾蘭(Pat Helland)的經典論文Life Beyond Transactions能獲很得大啓發。
基本原語(Basic primitives)。在分佈式系統中幾乎沒有一致認同的基本構建模塊,但目前在愈來愈多地在出現。好比Leader選舉,能夠參考Bully算法;分佈式狀態機複製,能夠參考維基百科和Lampson的論文,後者更權威,只是有些枯燥。
基本結論(Fundamental Results)。某些事實是須要吸取理解的,有幾點:若是進程之間可能丟失某些消息,那麼不可能在實現一致性存儲的同時響應全部的請求,這就是CAP定理;一致性不可能同時知足如下條件:a. 老是正確,b. 在異步系統中只要有一臺機器發生故障,系統老是能終止運行——中止失敗(FLP不可能性);通常而言,消息交互少於兩輪都不可能達成共識(Consensus)。
真實系統(Real systems)。學習分佈式系統架構最重要的是,結合一些真實系統的描述,反覆思考和點評其背後的設計決策。如谷歌的GFS、Spanner、Chubby、BigTable、Dapper等,以及Dryad、Cassandra和Ceph等非谷歌系統。
FLP不可能性的名稱起源於它的三位做者,Fischer、Lynch和Paterson。它是關於理論上能作出的功能最強的共識算法會受到怎樣的限制的討論。
所謂共識問題,就是讓網絡上的分佈式處理者最後都對同一個結果值達成共識。該解決方案對錯誤有恢復能力,處理者一旦崩潰之後,就再也不參與計算。在同步環境下,每一個操做步驟的時間和網絡通訊的延遲都是有限的,要解決共識問題是可能的,方式是:等待一個完整的步長來檢測某個處理者是否已失敗。若是沒有收到回覆,那就假定它已經崩潰。
共識問題有幾個變種,它們在「強度」方面有所不一樣——一般,一個更「強」問題的解決方案同時也能解決比該問題更「弱」的問題。共識問題的一個較強的形式以下。
給出一個處理者的集合,其中每個處理者都有一個初始值:
這三個特性分別被稱爲「終止」、「一致贊成」和「有效性」。任何一個具有這三點特性的算法都被認爲是解決了共識問題。
FLP不可能性則討論了異步模型下的狀況,主要結論有兩條。
在異步模型下不存在一個徹底正確的共識算法。不只上述較「強」形式的共識算法不可能實現,FLP還證實了比它弱一些的、只須要有一些無錯誤的進程作決定就足夠的共識算法也是不可能實現的。
在異步模型下存在一個部分正確的共識算法,前提是全部無錯誤的進程都總能作出一個決定,此外沒有進程會在它的執行過程當中死亡,而且初始狀況下超過半數進程都是存活狀態。
FLP的結論是,在異步模型中,僅一個處理者可能崩潰的狀況下,就已經沒有分佈式算法能解決共識問題。這是該問題的理論上界。其背後的緣由在於,異步模型下對於一個處理者完成工做而後再回復消息所需的時間並無上界。所以,沒法判斷出一個處理者究竟是崩潰了,仍是在用較長的時間來回復,或者是網絡有很大的延遲。
FLP不可能性對咱們還有別的啓發。一是網絡延遲很重要,網絡不能長時間處於擁塞狀態,不然共識算法將可能由於網絡延遲過長而致使超時失敗。二是計算時間也很重要。對於須要計算共識的處理過程(進程),如分佈式數據庫提交,須要在短期裏就計算出可否提交的結果,那就要保證計算結點資源充分,特別是內存容量、磁盤空閒時間和CPU時間方面要足夠,並在軟件層面確保計算不超時。
另外一個問題是,像Paxos這樣的共識算法爲何可行?實際上它並不屬於FLP不可能性證實中所說的「徹底正確」的算法。它的正確性會受超時值的影響。但這並不妨礙它在實踐中有效,由於咱們能夠經過避免網絡擁塞等手段來保證超時值是合適的。
它是分佈式系統基礎課的課程提綱,也是一份很棒的分佈式系統介紹,幾乎涵蓋了全部知識點,並輔以簡潔並切中要害的說明文字,很是適合初學者提綱挈領地瞭解知識全貌,快速與現有知識結合,造成知識體系。此外,還能夠把它做爲分佈式系統的知識圖譜,根據其中列出的知識點一一搜索,你能學會全部的東西。
這是一本免費的電子書。做者撰寫此書的目的是但願以一種更易於理解的方式,講述以亞馬遜的Dynamo、谷歌的BigTable和MapReduce等爲表明的分佈式系統背後的核心思想。
於是,書中着力撰寫分佈式系統中的關鍵概念,以便讓讀者可以快速瞭解最爲核心的知識,而且進行了足夠詳實的講述,方便讀者體會和理解,又不至於陷入細節。
全書分爲五章,講述了擴展性、可用性、性能和容錯等基礎知識,FLP不可能性和CAP定理,探討了大量的一致性模型;討論了時間和順序,及時鐘的各類用法。隨後,探討了複製問題,如何防止差別,以及如何接受差別。此外,每章末尾都給出了針對本章內容的擴展閱讀資源列表,這些資料是對本書內容的很好補充。
本書是由計算機科學家安德魯·斯圖爾特·塔能鮑姆(Andrew S. Tanenbaum)和其同事馬丁·範·斯蒂恩(Martin van Steen)協力撰寫的,是分佈式系統方面的經典教材。
語言簡潔,內容通俗易懂,介紹了分佈式系統的七大核心原理,並給出了大量的例子;系統講述了分佈式系統的概念和技術,包括通訊、進程、命名、同步化、一致性和複製、容錯以及安全等;討論了分佈式應用的開發方法(即範型)。
但本書不是一本指導「如何作」的手冊,僅適合系統性地學習基礎知識,瞭解編寫分佈式系統的基本原則和邏輯。中文翻譯版爲《分佈式系統原理與範型》(第二版)。
這是一本免費的在線小冊子,其中文翻譯版爲可擴展的Web架構和分佈式系統。
本書主要針對面向的互聯網(公網)的分佈式系統,但其中的原理或許也能夠應用於其餘分佈式系統的設計中。做者的觀點是,經過了解大型網站的分佈式架構原理,小型網站的構建也能從中受益。本書從大型互聯網系統的常見特性,如高可用、高性能、高可靠、易管理等出發,引出了一個相似於Flickr的典型的大型圖片網站的例子。
首先,從程序模塊化易組合的角度出發,引出了面向服務架構(SOA)的概念。同時,引伸出寫入和讀取二者的性能問題,及對此兩者如何調度的考量——在當今的軟硬件架構上,寫入幾乎老是比讀取更慢,包括軟件層面引發的寫入慢(如數據庫的一致性要求和B樹的修改)和硬件層面引發的寫入慢(如SSD)。
網絡提供商提供的下載帶寬也一般比上傳帶寬更大。讀取每每能夠異步操做,還能夠作gzip壓縮。寫入則每每須要保持鏈接直到數據上傳完成。所以,每每咱們會想把服務作成讀寫分離的形式。而後經過一個Flickr的例子,介紹了他們的服務器分片式集羣作法。
接下來說了冗餘。數據的冗餘異地備份(如master-slave)、服務的多版本冗餘、避免單點故障等。
隨後,在冗餘的基礎上,講了多分區擴容,亦即橫向擴容。橫向擴容是在單機容量沒法知足需求的狀況下不得不作的設計。但橫向擴容會帶來一個問題,即數據的局域性會變差。原本數據能夠存在於同一臺服務器上,但如今數據不得不存在於不一樣服務器上,潛在地下降了系統的性能(主要是可能延長響應時間)。另外一個問題是多份數據的不一致性。
以後,本書開始深刻講解數據訪問層面的設計。首先拋出一個大型數據(TB級以上)的存儲問題。若是內存都沒法緩存該數據量,性能將大幅降低,那麼就須要緩存數據。數據能夠緩存在每一個節點上。
但若是爲全部節點使用負載均衡,那麼分配到每一個節點的請求將十分隨機,大大下降緩存命中率,從而致使低效的緩存。接下來考慮全局緩存的設計。再接下來考慮分佈式緩存的設計。進一步,介紹了Memcached,以及Facebook的緩存設計方案。
代理服務器則能夠用於把多個重複請求合併成一個,對於公網上的公共服務來講,這樣作能夠大大減小對數據層訪問的次數。Squid和Varnish是兩個可用於生產的代理服務軟件。
當知道所須要讀取的數據的元信息時,好比知道一張圖片的URL,或者知道一個要全文搜索的單詞時,索引就能夠幫助找到那幾臺存有該信息的服務器,並從它們那裏獲取數據。文中擴展性地討論了本話題。
接下來談負載均衡器,以及一些典型的負載均衡拓撲。而後討論了對於用戶會話數據如何處理。好比,對於電子商務網站,用戶的購物車在沒有下單以前都必須保持有效。
一種辦法是讓用戶會話與服務器產生關聯,但這樣作會較難實現自動故障轉移,如何作好是個問題。另外,什麼時候該使用負載均衡是個問題。有時節點數量少的狀況下,只要使用輪換式DNS便可。負載均衡也會讓在線性能問題的檢測變得更麻煩。
對於寫入的負載,能夠用隊列的方式來減小對服務器的壓力,保證服務器的效率。消息隊列的開源實現有不少,如RabbitMQ、ActiveMQ、BeanstalkD,但有些隊列方案也使用瞭如Zookeeper,甚至是像Redis這樣的存儲服務。
本書主要講述了高性能互聯網分佈式服務的架構方案,並介紹了許多實用的工具。做者指出這是一個使人興奮的設計領域,雖然只講了一些皮毛,但這一領域不只如今有不少創新,未來也會愈來愈多。
本書是蘇黎世聯邦理工學院的教材。它講述了多種分佈式系統中會用到的算法。雖然分佈式系統的不一樣場景會用到不一樣算法,但並不表示這些算法都會被用到。不過,對於學生來講,掌握了算法設計的精髓也就能觸類旁通地設計出解決其餘問題的算法,從而獲得分佈式系統架構設計中所需的算法。
本書覆蓋的算法有:
這些算法對你邁向更高級更廣闊的技術領域真的至關有幫助的。
這本書的書名直譯過來是在有軟件錯誤的狀況下,構建可靠的分佈式系統,Erlang之父喬·阿姆斯特朗(Joe Armstrong)的力做。書中撰寫的內容是從1981年開始的一個研究項目的成果,這個項目是尋找更好的電信應用編程方式。
當時的電信應用都是大型程序,雖然通過了仔細的測試,但投入使用時程序中仍會存在大量的錯誤。做者及其同事假設這些程序中確實有錯誤,而後想法設法在這些錯誤存在的狀況下構建可靠的系統。他們測試了全部的編程語言,沒有一門語言擁有電信行業所須要的全部特性,因此促使一門全新的編程語言Erlang的開發,以及隨之出現的構建健壯系統(OTP)的設計方法論和庫集。
書中抽象了電信應用的全部需求,定義了問題域,講述了系統構建思路——模擬現實,簡單通用,並給出了指導規範。阿姆斯特朗認爲,在存在軟件錯誤的狀況下,構建可靠系統的核心問題能夠經過編程語言或者編程語言的標準庫來解決。因此本書有很大的篇幅來介紹Erlang,以及如何運用其構建具備容錯能力的電信應用。
雖然書中的內容是以構建20世紀80年代的電信系統爲背景,可是這種大規模分佈式的系統開發思路,以及對系統容錯能力的核心需求,與互聯網時代的分佈式系統架構思路出奇一致。書中對問題的抽象、總結,以及解決問題的思路和方案,有深入的洞察和清晰的闡釋,因此此書對如今的項目開發和架構有極強的指導和借鑑意義。
這是一本很是好的書。咱們知道,在分佈式的世界裏,數據結點的擴展是一件很是麻煩的事。而這本書則深刻淺出地用不少工程案例講解了如何讓數據結點作擴展。
做者馬丁·科勒普曼(Martin Kleppmann)在分佈式數據系統領域有着很深的功底,並在這本書中完整地梳理各種紛繁複雜設計背後的技術邏輯,不一樣架構之間的妥協與超越,很值得開發人員與架構設計者閱讀。
這本書深刻到B-Tree、SSTables、LSM這類數據存儲結構中,而且從外部的視角來審視這些數據結構對NoSQL和關係型數據庫所產生的影響。它可讓你很清楚地瞭解到真正世界的大數據架構中的數據分區、數據複製的一些坑,並提供了很好的解決方案。
最讚的是,做者將各類各樣的技術的本質很是好地關聯在一塊兒,幫你舉一反三。並且抽絲剝繭,循循善誘,從「提出問題」,到「解決問題」,到「解決方案」,再到「優化方案」和「對比不一樣的方案」,一點一點地把很是晦澀的技術和知識展開。
本書的引用至關多,每章後面都有幾百個Reference。經過這些Reference,你能夠看到更爲廣闊更爲精彩的世界。
這本書是2017年3月份出版的,目前尚未中譯版,不過英文也不難讀。很是推薦。這裏有這本書的PPT,你可從這個PPT中管中窺豹一下。
在今天的文章中,我給出了一些分佈式系統的基礎理論知識和幾本很不錯的圖書和資料,須要慢慢消化吸取。也許你看到這麼龐大的書單和資料列表有點望而卻步,可是我真的但願你可以花點時間來看看這些資料。相信你看完這些資料後,必定能上一個新的臺階。再加上一些在工程項目中的實踐,我保證你,必定能達到大多數人難以企及的技術境界。