分佈式架構高可用與高併發那些在工做中經常使用到的那些變態應用

反向代理服務

上圖展現了一個典型的三層架構的高性能 Web 應用。這種成熟的架構多年以來已被普遍部署於包括 Google、Yahoo、Facebook、Twitter、Wikipedia 在內的諸多大型 Web 應用中。前端

位於三層構架中最外層的反向代理服務器負責接受用戶的接入請求,在實際應用中,代理服務器一般至少還要完成如下列表中的一部分任務:web

鏈接管理:分別維護客戶端和應用服務器的鏈接池,管理並關閉已超時的長鏈接。redis

攻擊檢測和安全隔離:因爲反向代理服務無需完成任何動態頁面生成任務,全部與業務邏輯相關的請求都轉發至後端應用服務器處理。所以反向代理服務幾乎不會被應用程序設計或後端數據漏洞所影響。反向代理的安全性和可靠性一般僅取決於產品自己。在應用服務的前端部署反向代理服務器能夠有效地在後端應用和遠程用戶間創建起一套可靠的安全隔離和攻擊檢測機制。算法

若是須要的話,還能夠經過在外網、反向代理、後端應用和數據庫等邊界位置添加額外的硬件防火牆等網絡隔離設備來實現更高的安全性保證。數據庫

負載均衡:一般使用輪轉(Round Robin)或最少鏈接數優先等策略完成基於客戶請求的負載均衡;也可使用 SSI 等技術將一個客戶請求拆分紅若干並行計算部分分別提交到多個應用服務器。後端

分佈式的 cache 加速:能夠將反向代理分組部署在距離熱點地區地理位置較近的網絡邊界上。經過在位於客戶較近的位置提供緩衝服務來加速網絡應用。這實際上就構成了 CDN 網絡。緩存

靜態文件伺服:當收到靜態文件請求時,直接返回該文件而無需將該請求提交至後端應用服務器。安全

動態響應緩存:對一段時間內不會發生改變的動態生成響應進行緩存,避免後端應用服務器頻繁執行重複查詢和計算。服務器

數據壓縮傳輸:爲返回的數據啓用 GZIP/ZLIB 壓縮算法以節約帶寬。網絡

數據加密保護(SSL Offloading):爲與客戶端的通訊啓用 SSL/TLS 加密保護。

容錯:跟蹤後端應用服務器的健康情況,避免將請求調度到發生故障的服務器。

用戶鑑權:完成用戶登錄和會話創建等工做。

URL別名:對外創建統一的url別名信息,屏蔽真實位置。

應用混搭:經過SSI和URL映射技術將不一樣的web應用混搭在一塊兒。

協議轉換:爲使用 SCGI 和 FastCGI 等協議的後端應用提供協議轉換服務。

目前比較有名的反向代理服務包括:Apache httpd+mod_proxy / IIS+ARR / Squid / Apache Traffic Server / Nginx / Cherokee / Lighttpd / HAProxy 以及 Varnish 等等。

應用服務

應用服務層位於數據庫等後端通用服務層與反向代理層之間,向上接收由反向代理服務轉發而來的客戶端訪問請求,向下訪問由數據庫層提供的結構化存儲與數據查詢服務。

應用層實現了 Web 應用的全部業務邏輯,一般要完成大量的計算和數據動態生成任務。應用層內的各個節點不必定是徹底對等的,還可能以 SOA、μSOA 等架構拆分爲不一樣服務集羣。

上圖給出了一個典型的高併發、高性能應用層節點工做模型。每一個 Web 應用節點(在圖 5中由標有"App"字樣的方框表示)一般都會工做在本身的服務器(物理服務器或VPS)之上,多個應用節點能夠有效地並行工做,以方便地實現橫向擴展。

1、在上圖所示的例子中,Web 應用節點由 IO 回調線程池、Web 請求隊列以及後臺工做線程池等三個重要部分組成,其伺服流程以下:

2、當一個 Web 請求到達後,底層操做系統經過 IOCP、epoll、kqueue、event ports、real time signal (posix aio)、/dev/poll、pollset 等各種與具體平臺緊密相關的 IO 完成(或 IO 就緒)回調機制通知 AIO(Asynchronous IO)回調線程,對這個已到達的 Web 請求進行處理。

3、在 AIO 回調池中的工做線程接收到一個已到達的 Web 請求後,首先嚐試對該請求進行預處理。在預處理過程當中,將會使用位於本地的高速緩存來避免成本較高的數據庫查詢。若是本地緩存命中,則直接將緩存中的結果(仍然以異步 IO 的方式)返回客戶端,並結束本次請求。

3、若是指定的 Web 請求要求查詢的數據沒法被本地緩存命中,或者這個 Web 請求須要數據庫寫入操做,則該請求將被 AIO 回調線程追加到指定的隊列中,等待後臺工做線程池中的某個空閒線程對其進行進一步處理。

4、後臺工做線程池中的每一個線程都分別維護着兩條長鏈接:一條與底層到數據庫服務相連,另外一條則鏈接到分佈式緩存(memcached)網絡。經過讓每一個工做線程維護屬於本身的長鏈接,後臺工做線程池實現了數據庫和分佈式緩存鏈接池機制。長鏈接(Keep-Alive)經過爲不一樣的請求重複使用同一條網絡鏈接大大提升了應用程序處理效率和網絡利用率。

5、後臺工做線程在 Web 請求隊列上等待新的請求到達。在從隊列中取出一個新的請求後,後臺工做線程首先嚐試使用分佈式緩存服務命中該請求中的查詢操做,若是網絡緩存未命中或該請求須要數據庫寫入等進一步處理,則直接經過數據庫操做來完成這個 Web 請求。

6、當一個 Web 請求被處理完成後,後臺工做線程會將處理結果做爲 Web 響應以異步 IO 的方式返回到指定客戶端。

上述步驟粗略描述了一個典型 Web 應用節點的工做方式。值得注意的是,因爲設計思想和具體功能的差別,不一樣的 Web 應用間,不管在工做模式或架構上均可能存在很大的差別。

須要說明的是,與 epoll/kqueue/event ports 等相位觸發的通知機制不一樣,對於 Windows IOCP 和 POSIX AIO Realtime Signal 這類邊緣觸發的 AIO 完成事件通知機制。

爲了不操做系統底層 IO 完成隊列(或實時信號隊列)過長或溢出致使的內存緩衝區被長時間鎖定在非分頁內存池,在上述系統內的 AIO 回調方式其實是由兩個獨立的線程池和一個 AIO 完成事件隊列組成的:一個線程池專門負責不間斷地等待系統 AIO 完成隊列中到達的事件,並將其提交到一個內部的 AIO 完成隊列中(該隊列工做在用戶模式,具備用戶可控的彈性尺寸,而且不會鎖定內存);

與此同時另外一個線程池等待在這個內部 AIO 完成隊列上,而且處理不斷到達該隊列的 AIO 完成事件。

這樣的設計下降了操做系統的工做負擔,避免了在極端狀況下可能出現的消息丟失、內存泄露以及內存耗盡等問題,同時也能夠幫助操做系統更好地使用和管理非分頁內存池。

做爲典型案例:包括搜索引擎、Gmail 郵件服務在內的大部分 Google Web 應用均是使用 C/C++ 實現的。得益於 C/C++ 語言的高效和強大,Google 在爲全球 Internet 用戶提供最佳 Web 應用體驗的同時,也實現了在其遍佈全球的上百萬臺分佈式服務器上完成一次 Web 搜索,總能耗僅需 0.0003 kW·h 的優異表現。

數據庫和memcached服務

數據庫服務爲上層 Web 應用提供關係式或結構化的數據存儲與查詢支持。取決於具體用例,Web 應用可使用數據庫鏈接器之類的插件機制來提供對不一樣數據庫服務的訪問支持。在這種架構下,用戶能夠靈活地選擇或變動最適合企業現階段狀況的不一樣數據庫產品。

例如:用戶能夠在原型階段使用 SQLite 之類的嵌入式引擎完成快速部署和功能驗證;而在應用的初期階段切換到廉價的 MySql 數據庫解決方案;等到業務需求不斷上升,數據庫負載不斷加劇時再向 Clustrix、MongoDB、Cassandra、MySql Cluster、ORACLE 等更昂貴和複雜的解決方案進行遷移。

Memcached 服務做爲一個徹底基於內存和 <Key, Value> 對的分佈式數據對象緩衝服務,擁有使人難以置信的查詢效率以及一個優雅的,無需服務器間通訊的大型分佈式架構。

對於高負載 Web 應用來講,Memcached 常被用做一種重要的數據庫訪問加速服務,所以它不是一個必選組件。用戶徹底能夠等到現實環境下的數據庫服務出現了性能瓶頸時在部署它。

值得強調的是,雖然 memcached 並非一個必選組件,但經過其在 YouTube、Wikipedia、Amazon.com、SourceForge、Facebook、Twitter 等大型 Web 應用上的多年部署能夠證實:memcached 不但可以在高負載環境下長期穩定地工做,並且能夠戲劇性地提高數據查詢的總體效率。

固然,咱們也應該注意到:以 memcached 爲表明的分佈式緩存系統,其本質上是一種以犧牲一致性爲代價來提高平均訪問效率的妥協方案——緩存服務爲數據庫中的部分記錄增長了分佈式副本。對於同一數據的多個分佈式副原本說,除非使用 Paxos、Raft 等一致性算法,否則沒法實現強一致性保證。

矛盾的是,memory cache 自己就是用來提高效率的,這使得爲了它使用上述開銷高昂的分佈式強一致性算法變得很是不切實際:目前的分佈式強一致性算法均要求每次訪問請求(不管讀寫)都須要同時訪問包括後臺數據庫主從節點在內的多數派副本——顯然,這還不如干脆不使用緩存來的有效率。

另外,即便是 Paxos、Raft 之類的分佈式一致性算法也只能在單個記錄的級別上保證強一致。意即:即便應用了此類算法,也沒法憑此提供事務級的強一致性保證。

除此以外,分佈式緩存也增長了程序設計的複雜度(須要在訪問數據庫的同時嘗試命中或更新緩存),而且還增長了較差情形下的訪問延遲(如:未命中時的 RTT 等待延遲,以及節點下線、網絡通訊故障時的延遲等)。

與此同時,能夠看到:從二十年前開始,各主流數據庫產品其實均早已實現了成熟、高命中率的多層(磁盤塊、數據頁、結果集等)緩存機制。既然分佈式緩存有如此多的缺陷,而數據庫產品又自帶了優秀的緩存機制,它爲什麼又可以成爲現代高負載 Web App 中的重要基石呢?

其根本緣由在於:對於十年前的技術環境來講,當時十分缺少橫向擴展能力的 RDBMS(SQL)系統已成爲了嚴重製約 Web App 等網絡應用擴大規模的瓶頸。爲此,以 Google BigTable、Facebook Cassandra、MongoDB 爲表明的 NoSQL 數據庫產品,以及以 memcached、redis 爲表明的分佈式緩存產品紛紛粉墨登場,並各自扮演了重要做用。

與 MySQL、ORACLE、DB二、MS SQL Server、PostgreSQL 等當時的 "傳統" SQL數據庫產品相比,不管 NoSQL 數據庫仍是分佈式緩存產品,其本質上都是以犧牲前者的強一致性爲代價,來換取更優的橫向擴展能力。

應當看到,這種取捨是在當時技術條件下作出的無奈、痛苦的抉擇,系統所以而變得複雜——在須要事務和強一致性保障,而且數據量較少的地方,使用無緩存層的傳統 RDBMS;在一致性方面有必定妥協餘地,而且讀多寫少的地方儘可能使用分佈式緩存來加速;在對一致性要求更低的大數據上使用 NoSQL;若是數據量較大,同時對一致性要求也較高,就只能嘗試經過對 RDMBS 分庫分表等方法來儘可能解決,爲此還要開發各類中間件來實現數據訪問的請求分發和結果集聚合等複雜操做……各類情形不一而足,而它們的相互組合和交織則再次加重了複雜性。

回顧起來,這是一箇舊秩序被打破,新秩序又還沒有創建起來的混亂時代——老舊 RMDBS 缺少橫向擴展能力,沒法知足新時代的大數據處理需求,又沒有一種可以替代老系統地位,可同時知足大部分用戶需求的普適級結構化數據管理方案。

這是一個青黃不接的時代,而 BigTable、Cassandra、memcached 等產品則分別是 Google、Facebook 以及 LiveJournal 等廠商在那個時代進行 "自救" 的結果。這樣以:"花費最小代價,知足自身業務需求便可" 爲目標的產物天然不太容易具有很好的普適性。

然而現在,咱們終於就快要走出這個窘境。隨着 Google F一、MySQL Cluster(NDB)、Clustrix、VoltDB、MemSQL、NuoDB 等衆多 NewSQL 解決方案的逐步成熟以及技術的不斷進步,橫向擴展能力逐漸再也不成爲 RDBMS 的瓶頸。今天的架構設計師徹底能夠在確保系統擁有足夠橫向擴展能力的同時,實現分佈式的事務級(XA)強一致性保證:

如上圖所示,在 NewSQL 具有了良好的橫向擴展能力後,架構中再也不迫切須要分佈式緩存和 NoSQL 產品來彌補這方面的短板,這使得設計和開發工做再次迴歸到了最初的簡潔和清晰。而對象存儲(Object Storage)服務則提供了對音頻、視頻、圖片、文件包等海量非結構化BLOB數據的存儲和訪問支持。

這樣簡潔、清晰、樸素的架構使一切看起來彷彿迴歸到了多年之前,對象存儲服務就像 FAT、NTFS、Ext3 等磁盤文件系統,NewSQL 服務則好像當年 MySQL、SQL Server 等 "單機版" 數據庫。但一切卻又已不一樣,業務邏輯、數據庫和文件存儲均已演進成爲支持橫向擴展的高可用集羣,在性能、容量、可用性、可靠性、可伸縮性等方面有了巨大的飛躍:人類老是以螺旋上升的方式不斷進步——在每一次看似迴歸的變遷中,實包含了本質的昇華。

隨着 GlusterFS、Ceph、Lustre 等可 mount 且支持 Native File API 的分佈式文件系統愈來愈成熟和完善,也有望於大部分場合下逐漸替換現有的對象存儲服務。至此 Web App 架構的演進才能算是完成了一次重生——這還算不上是涅槃,當咱們可以在真正意義上實現出高效、高可用的多虛一(Single System Image)系統時,涅槃才真正降臨。那時的咱們編寫分佈式應用與現在編寫一個單機版的多線程應用將不會有任何區別——進程自然就是分佈式、高可用的!

三層架構的可伸縮性

小到集中部署於單臺物理服務器或 VPS 內,大到 Google 遍佈全球的上百萬臺物理服務器所組成的分佈式應用。前文描述的三層 Web 應用架構體現出了難以置信的可伸縮性。

具體來講,在項目驗證、應用部署和服務運營的初期階段,能夠將以上三層服務組件集中部署於同一臺物理服務器或 VPS 內。與此同時,可經過取消 memcached 服務,以及使用資源開銷小而且易於部署的嵌入式數據庫產品來進一步下降部署難度和系統總體資源開銷。

隨着項目運營的擴大和負載的持續加劇,當單服務器方案和簡單的縱向擴展已沒法知足項目運營負荷時,用戶便可經過將各組件分佈式地運行在多臺服務器內來達到橫向擴展的目的。

例如:反向代理可經過 DNS CNAME 記錄輪轉或 3/4 層轉發(LVS、HAProxy等)的方式實現分佈式負載均衡。應用服務則可由反向代理使用基於輪轉或最小負載優先等策略來實現分佈式和負載均衡。此外,使用基於共享 IP 的服務器集羣方案也可以實現負載均衡和容錯機制。

與此相似,memcached 和數據庫產品也都有本身的分佈式運算、負載均衡以及容錯方案。此外,數據庫訪問性能瓶頸可經過更換非關係式(NoSQL)的數據庫產品,或使用主-從數據庫加複製等方式來提高。而數據庫查詢性能則可經過部署 memcached 或相似服務來極大程度地改善。

寫在最後

歡迎你們關注個人公衆號【風平浪靜如碼】,海量Java相關文章,學習資料都會在裏面更新,整理的資料也會放在裏面。

以爲寫的還不錯的就點個贊,加個關注唄!點關注,不迷路,持續更新!!!

相關文章
相關標籤/搜索