以下內容都是騰訊WeTest《淺析分布式系統》博文的摘抄,說是摘抄其實跟原文照搬差不了多少,原文地址騰訊WeTest淺析分布式系統
1. 什么是分布式系統
要理解這個問題可以從它的適用場景入手,一個非常適用于高承載量互聯網業務的系統。什么叫高承載量,簡單地可以理解為就是業務有非常高的被訪問頻率,如一個網站或者一個APP或者一網游或視頻直播等每天有百萬千萬甚至億級別用戶訪問。
當一個互聯網業務架構不能夠滿足高承載量的需求時,對于用戶的感受就是:網頁打開慢,游戲或者視頻卡頓。
2. 分布式系統的特性
一個分布式系統應該具備以下幾點特性:
高吞吐、高并發、低延遲和負載均衡。
高吞吐
高吞吐指的是業務系統可以同時承載大量的用戶,關注的是整個系統能同時服務的用戶數,它們由多臺服務器協作一起完成。高并發
高并發指的是業務系統在承載海量用戶的時候,每臺服務器程序都能夠盡量多的同時處理多個任務。低延遲
低延遲指的是業務系統在面對海量用戶時仍能夠很快的返回計算結果。
如果業務系統架構不合理,當有大量用戶同時訪問系統時很可能造成請求排隊,當排隊長度過長,還會導致系統內存耗盡、帶寬被占滿等問題。如果因為排隊失敗在采取重試的策略,則會進一步增加延遲。
一個合理的分布式系統會采用將用戶請求進行分揀和分發的做法,盡快的讓更多的服務器來處理用戶的請求,但如果分發的層次過多又會增加系統的延遲。負載均衡
互聯網業務面向整個互聯網的用戶,他們位于不同時區,不同地理位置,所使用的網絡線路也不一樣,考慮到這種情況,分布式系統就需要在不同的地理位置和網絡線路中部署服務器。這些不同位置的服務器依然屬于一個分布式系統,他們同時處理不同用戶相同的業務請求,這即實現了負載均衡。
3. 分布式系統如何承載海量業務
網站業務利用DNS多個IP的A記錄提高業務承載量
這里不談DNS整個分布式系統的工作方式,只說下DNS配置A記錄時指定多個IP地址,當用戶請求某網站的IP地址時,隨機解析一個IP給他,另一用戶在去解析這一網站,DNS隨機解析另外的IP給這一用戶,這樣具備不同IP地址的服務器就可以共同承載同一網站的業務。
但是上面這種方式,在很多互聯網業務中并不可行。如需要用戶進行登錄的業務,用戶在登錄某一服務器后發起多個請求,如果把這些請求隨機轉發到不同的服務器上,那用戶的登錄狀態會丟失,造成一些請求處理失敗。這時就要考慮將用戶的cookie或者登錄憑據轉發給其它服務器,以實現會話保持效果。分布式系統邏輯
典型的三層式分布式系統邏輯包括:接入層、邏輯層、數據層。
- 分布式業務架構示意圖
在實際的業務架構中,分布式系統會設計成多個層次的,為了把請求交給正確的進程處理,需要設計很多專門用于轉發請求的進程和服務器,這些進程常以Proxy或者Router命名。這些代理很多時候是通過TCP來連接前端和后端,但是tcp有故障后不容易恢復的問題且其網絡編程復雜,所以有了更好的進程間通訊機制----消息隊列。
為了讓分層模式(代理、路由)變得高效簡單,又采用了很多相關技術。如:
-
并發模型
計算機中大部分的程序都會處理同時到達的多個請求。程序會同時獲得多個輸入,需要返回多個輸出。在這個處理過程中,還會碰到等待和阻塞的情況,如程序要等待數據庫處理完成,等待向另一個進程請求結果等。如果把這些請求一個挨著一個地處理,這些空閑的等待時間將白白浪費,造成用戶的響應延遲增加,系統的吞吐量極度下降。所以計算機程序會使用并發模型,同時處理多個請求,常用的并發模型技術有2種。-
多線程
多線程的代碼,每個線程中的代碼是按先后順序執行的,但由于同時運行著多個線程,需要給很多數據加鎖,避免對同一個數據的處理邏輯出錯。但這些鎖又可能導致線程出現死鎖。多線程的不足:在多線程模型中,線程反復切換會導致不必要的開銷,每個線程都需要一個獨立的棧空間,在多線程并行運行的時候,這些棧的數據可能需要來回拷貝,需要額外消耗CPU,同時線程越多占用的內存空間也越多。異步回調模型可以解決這些問題。
異步回調模型
異步回調基于非阻塞的I/O操作實現,在代碼中就表現為在調用讀寫函數的時候不會卡在那一句函數調用而是立即返回有無數據的結果。Linux系統中的epoll技術,就是利用底層內核的機制,可以快速查找到有數據可以讀寫的連接、文件。由于每個操作都是非阻塞的,所以一個進程就可以處理大量并發的請求。也正因為只有一個進程,所以不會出現多線程中兩個函數的語句交錯執行需要數據鎖以保證執行不會出錯。
異步非阻塞技術,沒有了數據鎖,也沒有多線程間頻繁切換的額外開銷,所以它很適合對吞吐量和并發數有較高要求的系統。
-
緩沖技術
緩沖技術可以降低業務延遲。適用場景
如一個網站如果每個HTTP請求都需要去讀寫mysql數據庫,數據庫很快就會回為連接數占滿而出現停止響應,網站就會出現人一多就卡死的情況。一般情況下數據庫允許的最大連接數要遠小于web應用的并發連接數的。為了減少對數據庫的連接和訪問,可以將數據庫中查詢的結果存放到更快的設備上,如果沒有相關聯的修改,就可以直接用緩存去響應客戶端請求。最典型的緩沖系統
Memcache是最典型的web應用緩沖系統。當用戶請求過來時,先檢查Memcache中是否有緩存,如果有則用緩存去響應請求,如果沒有則去后續查詢然后回應請求并會寫入Memcache中,下次有同樣的請求過來時就直接使用Memcache中的緩存響應請求。
Memcache不能直接組建一個集群系統,如果一個Memcache不夠用,就要手工用代碼去分配,哪些數據應該去哪個Memcache進程。
Memcache的每筆請求,都要經過網絡傳輸,才能去拉取內存中的數據,而沒有將請求者本身的內存利用起來。為了解決這個問題就可以利用LRU算法,把數據放在一個哈希表結構的堆內存中。
因為Memcache不支持集群,為了又能將緩存分布到不同的機器上,因此出現了讀寫分離技術,即緩存每次寫都寫到多個緩沖進程中去,但是讀的時候可以讀取任何一個進程。讀寫分離的方案很適用于有明顯讀寫不平衡的業務數據。
對于讀寫沒有明顯不平衡的業務系統如:社區、游戲,讀寫分離就不在適用于它們。在這些系統中要同時將請求方本地內存和遠端進程的內存緩存結合起來使用,就需要使用到“一致性哈希算法”,以實現一個數據不在同時寫往多個緩存進程上,而是按一定規律分布在多個進程上。這樣做的好處是當某一個進程失效時,不會影響整個集群中所有的緩存數據,其它緩存進程不用修改緩存記錄。
-
存儲技術
分布式系統有一個著名的CAP原則:
CAP原則又稱CAP定理,指的是在一個分布式系統中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分區容錯性),三者不可得兼。---------百度百科
在很多互聯網業務中并不需要很復雜的關系型數據庫,數據表來存儲數據,對于索引也只是需要能夠根據主索引搜索即可, 所以就產生了NoSQL數據庫,早期的有MangoDB,現在比較流行的是Redis。
NoSQL相較于SQL型數據庫具有更快、承載量更大的優勢。它們只需要按照一條索引來檢索和寫入數據,利用分布式進行部署,在部署時我們可以按這條主索引來定義數據存放的進程(服務器)。這樣就可以很方便的將數據存放在不同的服務器上,形成分布式部署。
4. 分布式系統在管理上存在的問題
-
硬件故障率
擁有眾多機器的分布式系統比只有一臺或者少量機器的系統,出現硬件故障的機率要高得多。所以分布式系統軟件在架構時要考慮到硬件故障的情況,在故障發生時,不要出現一臺服務器硬件故障導致事個服務器集群工作不正常。
分布式系統中網絡線路出現故障的機率也很高,這時要讓系統軟件具備自我維護和冗余的功能。
-
資源利用率
分布式系統中如果硬件資源不夠用了,可以對硬件進行擴容,增加更多的服務器,但軟件層面的擴容要復雜得多,可能需要停掉整個集群,然后修改各種配置才能重新啟動集群。
對于線上業務進行擴容,有可能會造成用戶數據丟失或者錯誤。所以線上擴容適用于無狀態的服務上,我們都知道HTTP就是屬于無狀態的連接,所以web服務一般可以進行在線擴容。但對于類似游戲這種有狀態的服務幾乎是不可能進行在線擴容的。
當硬件資源處于閑置狀態時,分布式系統還應該對硬件資源進行縮容,將這部分資源放入其它的業務系統中。
-
軟件服務內容更新
為了修改業務系統中的bug,或者發布新的功能等操作,要對軟件進行不斷的迭代升級。 在只有一臺或少量機器的業務系統中還好辦,只需要把軟件包拷貝過去,然后安裝配置即可,但在分布式系統中機器數量眾多,所以服務端軟件需要開發相應的軟件更新,版本升級的功能,預先對于配置文件、命令行參數、系統變量的使用做一些規劃,讓安裝部署的工具運行更快更可靠。
軟件版本升級,有時候還涉及到數據格式的更改,如修改數據表結構,所以軟件在架構之初就應該考慮到這些,做些預設準備。
客戶端程序的升級,有時候會涉及到跟服務器的通信協議改變的問題,而一般客戶端升級不會和服務器同步,這就會造成一個問題,網絡中需要為不同版本的客戶端部署不同的服務端系統。為了避免同時維護多套服務器系統,在軟件架構之初時應該使用“版本兼容”的協議定義方式。
-
數據統計
分布式系統的日志數據一般需要集中到一起,統一進行分析統計。當分布式系統規模越來越大時,這個日志數據的量會變得非常大,統計工作非常消耗計算機資源。經典的分布式統計模型有Google的Map Reduce模型,這種模型可以利用大量服務器進行統計工作,但實際使用中由于它統計的數據格式與常見的SQL差別很大,所以最后往往還需要借助MySQL進行更細層面的統計。
5. 解決分布式系統管理性問題的基本手段
-
目錄服務
一個自動化程度高的分布式系統會動態保存系統中的進程狀態,如自己負責的模塊、自己的負載情況、對某些數據的掌握等,還有進程和其它相關進程的對應關系,如IP地址和端口。這樣一來整個分布式系統才能讓程序自己去做容災和負載均衡。這個自動的過程就依賴于目錄服務。
一個目錄服務是用來記錄集群中運行的進程的狀態的。集群中的進程會自動和目錄服務關聯,這樣系統在容災、擴容、負載均衡時可以自動根據目錄服務里的數據來調整請求的目的地,從而繞開故障機器,連接新的服務器。
zookeeper是用來實現目錄服務的開源軟件。目錄服務需要進行集群部署以防單點故障導致整個分布式系統工作異常。zookeeper可以啟動奇數個進程,以組成一個集群。zookeeper的數據存儲結構,是一個類似文件目錄的樹狀系統,所以需要利用它的功能,把每個進程都綁定到其中一個分支上,通過檢查這些分支來進行請求的轉發,還可以在這些分支上標記負載狀態,以實現負載均衡。
-
消息隊列服務
計算機系統中兩個進程要跨機器通訊,需要用到tcp/udp協議,如果直接使用網絡API去編寫跨進程的程序,需要寫大量底層的socket代碼,需要處理如何找到交互數據的進程,如何保障數據包不至于丟失,如果通訊的對方進程掛了,或者進程要重啟應該如何處理,這些問題在程序開發時都要考慮到。
分布式系統中進程間更有效的通訊模型是消息隊列模型。這一模型就是把進程間的交互,抽象成對一個個消息的處理,這些消息本身都有一些隊列,也就是管道來對消息進行緩存,每個進程可以訪問一個或者多個隊列,從里面讀取消息(消費)或寫入消息(生產)。消息本身的路由,是由存放的隊列決定的,這樣就把復雜的路由問題,變成了如何管理靜態的隊列的問題。
-
事務系統
分布式系統中的一個事務可能分布在不同的進程上,任何一個進程都可能出現故障,要在分布式系統上解決事務問題,必須有兩個核心工具:一個是穩定的狀態存儲系統,另一個是方便可靠的廣播系統。
一個事務的處理過程都必須在整個集群中可見,需要將事務處理每步的狀態都寫到目錄服務上。
-
自動部署工具
分布式系統最大的需求,是在運行時進行服務容量的變更:擴容或者縮容。而在分布式系統中某些節點故障的時候,需要新的節點來恢復工作。在分布式系統中,一般采用池的方式來管理服務,在有需要的時候將某服務器在不同的服務間進行轉換。。
分布式系統中服務的變化需要用到自動的軟件部署工具,如Chef,這一可編程的通用部署系統,比rpm更簡便。
Docker也具備強大的自動部署能力,下圖為Docker的原理示意圖:
-
日志服務
服務器端的日志是開發測試,排除BUG,了解軟件運行情況非常有用的工具?,F代的分布式系統中日志會有一些標準化的規范:- 日志必須是一行一行的。
- 每行日志都應該有一些統一的頭部。
- 日志的輸出應該是分等級的(Debug、info、warn、error、fatal)。
- 日志的頭部應該有一些字段可以用于縮小日志查找范圍,如用戶ID、IP等(日志染色)。
- 日志還應該具有回滾功能,保持固定大小的多個文件。
在開源界有名為log4x的日志家族,最為出名的是log4j日志組件庫。
-
日志收集
在分布式系統中需要有一個系統專門用來收集各服務器上的日志文件,對它們進行統一集中管理,分析并產生預警信息,從而實現對整個分布式系統運行狀況的監控。由于日志文件眾多,一般會采用分布式文件系統來進行存放源源不斷到達的日志,這些日志一般通過UDP協議發送過來。然后在這個系統中還有一個類似Map Reduce的架構以實現對海量日志信息的快速分揀及預警。下圖為常用的分布式系統中日志服務的架構示意圖:
6. 分布式系統給開發帶來的挑戰及應對
分布式系統軟件開發除了滿足業務需求的功能外,還需要一些功能來讓多進程的系統穩定可靠地運行,這些功能的實現可以交由微服務框架來完成。
現在流行的微服務框架有EJB(企業JavaBean)、WebService。微服務框架包含了服務進程之間通訊所涉及的消息的路由、編碼解碼、服務狀態的讀寫等,在開發分布式系統軟件時候使用微服務框架可以簡化這一過程。 WebService則是把復雜的路由、編解碼等操作簡化成常見的一次HTTP操作。
分布式系統復雜之處就在于需要把容災、擴容、負載均衡等功能都融合到跨進程調用里。所以使用一套通用的代碼來為所有的跨進程通訊,統一的實現容災、擴容、負載均衡、過載保護、狀態緩存命中等非功能性需求,可以大大簡化整個分布式系統的復雜性。
微服務框架在路由階段會對整個集群所有節點的狀態進行觀察,如哪些地址上運行了哪些服務的進程,以及這些服務進程的負載如何,是否可用,然后對于有狀態的服務,還會使用一致性哈希算法,去盡量試圖提高緩存的命中率。當集群中的節點狀態發生變化的時候,微服務框架下的所有節點都能盡快的獲得這個變化的情況,重新規劃服務路由方向,從而實現自動化的路由選擇,避開那些負載過高或失效的節點。
-
異步編程工具
為了解決回調函數對于代碼可讀性的破壞作用,可以采用協程來進行改進,所謂的協程類似于多線程,但區別在于協程不會同時運行,它只是在需要阻塞的地方,用Yield()切換出去執行其他協程,然后當阻塞結束后,用Resume()回到剛剛切換的位置繼續往下執行。
Future/Promise模型也是用于改善回調函數的寫法,這種寫法的基本思路是”一次性把所有回調寫到一起”。
lamda模型是另一種改善回調函數的方法,lamda意味著閉包。
-
云服務模型
現在常用的云服務模型有三種:IaaS、PaaS、SaaS。
在分布式系統中要集中管理被不同的網絡和硬件切割成小塊的計算資源是比較難的。
隨著虛擬化技術的發展,可以把被分割的計算單元,更智能的統一起來,最常見的就是IaaS技術,當我們可以用一個服務器硬件運行多個虛擬的服務器操作系統時,需要維護的硬件數量就會成倍下降。而PaaS技術可以為某一種特定的編程模型,統一的進行系統運行環境的部署維護,而不在需要一臺臺服務器去安裝系統去部署應用軟件。
隨著業務模型成熟到可以抽象為一些固定的軟件時,分布式系統會變得更加易用,計算能力不再是代碼和庫,而是一個個通過網絡提供服務的云---SaaS,這樣使用者只需要申請一個接口,填上預期的容量額度就能直接使用了。
7. 分布式系統問題的解決方式匯總
8. 總結
騰訊WeTest的這篇干貨長文,對于接觸Linux生態圈還不長的我來說,實在是干貨滿滿。對于從整體上理解分布式系統很有幫助,所以近乎原版地將它摘抄在此,值得多讀幾次加深理解。