讀書筆記-大型網站技術架構:核心原理與案例分析

作者:李智慧

好評襲來

回顧網站架構的發展歷程,我們可以發現任何大型網站架構的發展都非一蹴而就的,同自然界生物物競天擇的自然進化規律一樣,大型網站的架構發展和演變也基本遵循着類似的規律。

推薦序二

正如本書中所言:“好的設計絕對不是模仿、不是生搬硬套某個模式,而是在對問題深刻理解之上的創造與創新,即使是‘微創新’,也是讓人耳目一新的似曾相識。山寨與創新的最大區別不在於是否抄襲、是否模仿,而在於對問題和需求是否真正理解與把握。”

1.1 大型網站軟件系統的特點

與傳統企業應用系統相比,大型互聯網應用系統有以下特點。

高併發,大流量:需要面對高併發用戶,大流量訪問。

高可用:系統7×24小時不間斷服務。

海量數據:需要存儲、管理海量數據,需要使用大量服務器。

用戶分佈廣泛,網絡情況複雜:許多大型互聯網都是爲全球用戶提供服務的,用戶分佈範圍廣,各地網絡情況千差萬別。

安全環境惡劣:由於互聯網的開放性,使得互聯網站更容易受到×××,大型網站幾乎每天都會被××××××。

需求快速變更,發佈頻繁:和傳統軟件的版本發佈頻率不同,互聯網產品爲快速適應市場,滿足用戶需求,其產品發佈頻率是極高的。

漸進式發展:與傳統軟件產品或企業應用系統一開始就規劃好全部的功能和非功能需求不同,幾乎所有的大型互聯網站都是從一個小網站開始,漸進地發展起來的。

好的互聯網產品都是慢慢運營出來的,不是一開始就開發好的,這也正好與網站架構的發展演化過程對應。

1.2 大型網站架構演化發展歷程

這三臺服務器對硬件資源的要求各不相同,應用服務器需要處理大量的業務邏輯,因此需要更快更強大的CPU;數據庫服務器需要快速磁盤檢索和數據緩存,因此需要更快的硬盤和更大的內存;文件服務器需要存儲大量用戶上傳的文件,因此需要更大的硬盤。

網站訪問特點和現實世界的財富分配一樣遵循二八定律:80%的業務訪問集中在20%的數據上。

既然大部分的業務訪問集中在一小部分數據上,那麼如果把這一小部分數據緩存在內存中,是不是就可以減少數據庫的訪問壓力,提高整個網站的數據訪問速度,改善數據庫的寫入性能了呢?

網站使用的緩存可以分爲兩種:緩存在應用服務器上的本地緩存和緩存在專門的分佈式緩存服務器上的遠程緩存。本地緩存的訪問速度更快一些,但是受應用服務器內存限制,其緩存數據量有限,而且會出現和應用程序爭用內存的情況。遠程分佈式緩存可以使用集羣的方式,部署大內存的服務器作爲專門的緩存服務器,可以在理論上做到不受內存容量限制的緩存服務,如圖1.3所示。

使用集羣是網站解決高併發、海量數據問題的常用手段。當一臺服務器的處理能力、存儲空間不足時,不要企圖去換更強大的服務器,對大型網站而言,不管多麼強大的服務器,都滿足不了網站持續增長的業務需求。這種情況下,更恰當的做法是增加一臺服務器分擔原有服務器的訪問及存儲壓力。

網站在使用緩存後,使絕大部分數據讀操作訪問都可以不通過數據庫就能完成,但是仍有一部分讀操作(緩存訪問不命中、緩存過期)和全部的寫操作需要訪問數據庫,在網站的用戶達到一定規模後,數據庫因爲負載壓力過高而成爲網站的瓶頸。

目前大部分的主流數據庫都提供主從熱備功能,通過配置兩臺數據庫主從關係,可以將一臺數據庫服務器的數據更新同步到另一臺服務器上。網站利用數據庫的這一功能,實現數據庫讀寫分離,從而改善數據庫負載壓力

爲了提供更好的用戶體驗,留住用戶,網站需要加速網站訪問速度。主要手段有使用CDN和反向代理

CDN和反向代理的基本原理都是緩存,區別在於CDN部署在網絡提供商的機房,使用戶在請求網站服務時,可以從距離自己最近的網絡提供商機房獲取數據;而反向代理則部署在網站的中心機房,當用戶請求到達中心機房後,首先訪問的服務器是反向代理服務器,如果反向代理服務器中緩存着用戶請求的資源,就將其直接返回給用戶。

分佈式數據庫是網站數據庫拆分的最後手段,只有在單表數據規模非常龐大的時候才使用。不到不得已時,網站更常用的數據庫拆分手段是業務分庫,將不同業務的數據庫部署在不同的物理服務器上。

隨着網站業務越來越複雜,對數據存儲和檢索的需求也越來越複雜,網站需要採用一些非關係數據庫技術如NoSQL和非數據庫查詢技術如搜索引擎。

通過使用分而治之的手段將整個網站業務分成不同的產品線,如大型購物交易網站就會將首頁、商鋪、訂單、買家、賣家等拆分成不同的產品線,分歸不同的業務團隊負責。

具體到技術上,也會根據產品線劃分,將一個網站拆分成許多不同的應用,每個應用獨立部署維護隨着業務拆分越來越小,存儲系統越來越龐大,應用系統的整體複雜度呈指數級增加,部署維護越來越困難。由於所有應用要和所有數據庫系統連接,在數萬臺服務器規模的網站中,這些連接的數目是服務器規模的平方,導致存數據庫接資源不足,拒絕服務。

許多相同的業務操作,比如用戶管理、商品管理等,那麼可以將這些共用的業務提取出來,獨立部署。由這些可複用的業務連接數據庫,提供共用業務服務,而應用系統只需要管理用戶界面,通過分佈式服務調用共用業務服務完成具體業務操作。

目前許多大型網站都開始建設雲計算平臺,將計算作爲一種基礎資源出售,中小網站不需要再關心技術架構問題,只需要按需付費,就可以使網站隨着業務的增長逐漸獲得更大的存儲空間和更多的計算資源。

1.3 大型網站架構演化的價值觀

在網站還很小的時候就去追求網站的架構是捨本逐末,得不償失的。小型網站最需要做的就是爲用戶提供好的服務來創造價值,得到用戶的認可,活下去,野蠻生長。

是業務成就了技術,是事業成就了人,而不是相反。

在業務問題還沒有理清楚的時候就從外面挖來許多技術高手,仿照成功的互聯網公司打造技術平臺,這無疑是南轅北轍,緣木求魚。

1.4 網站架構設計誤區

大公司的經驗和成功模式固然重要,值得學習借鑑,但如果因此而變得盲從,就失去了堅持自我的勇氣,在架構演化的道路上遲早會迷路。

網站技術是爲業務而存在的,除此毫無意義。在技術選型和架構設計中,脫離網站業務發展的實際,一味追求時髦的新技術,可能會將網站技術發展引入崎嶇小道,架構之路越走越難。

後來證明12306確實是朝這個方向發展的:在售票方式上引入了排隊機制、整點售票調整爲分時段售票。其實如果能控制住併發訪問的量,很多棘手的技術問題也就不是什麼問題了。

技術是用來解決業務問題的,而業務的問題,也可以通過業務的手段去解決。

2 大型網站架構模式

模式的關鍵在於模式的可重複性,問題與場景的可重複性帶來解決方案的可重複使用。

也許互聯網產品不是隨便複製就能成功的,創新的產品更能爲用戶創造價值。但是網站架構卻有一些共同的模式,這些模式已經被許多大型網站一再驗證,通過對這些模式的學習,我們可以掌握大型網站架構的一般思路和解決方案,以指導我們的架構設計。

2.1 網站架構模式

分層是企業應用系統中最常見的一種架構模式,將系統在橫向維度上切分成幾個部分,每個部分負責一部分相對比較單一的職責,然後通過上層對下層的依賴和調用組成一個完整的系統。

在大型網站架構中也採用分層結構,將網站軟件系統分爲應用層、服務層、數據層,如表2.1所示。

分層架構也有一些挑戰,就是必須合理規劃層次邊界和接口,在開發過程中,嚴格遵循分層架構的約束,禁止跨層次的調用(應用層直接調用數據層)及逆向調用(數據層調用服務層,或者服務層調用應用層)。

在實踐中,大的分層結構內部還可以繼續分層,如應用層可以再細分爲視圖層(美工負責)和業務邏輯層(工程師負責);服務層也可以細分爲數據接口層(適配各種輸入和輸出的數據格式)和邏輯處理層。

三層結構分別部署在不同的服務器上

在網站規模還很小的時候就應該採用分層的架構,這樣將來網站做大時纔能有更好地應對。

如果說分層是將軟件在橫向方面進行切分,那麼分割就是在縱向方面對軟件進行切分。

這些模塊不管在邏輯上還是物理部署上,都可以是獨立的。同樣在服務層也可以根據需要將服務分割成合適的模塊。

對於大型網站,分層和分割的一個主要目的是爲了切分後的模塊便於分佈式部署,即將不同模塊部署在不同的服務器上,通過遠程調用協同工作。分佈式意味着可以使用更多的計算機完成同樣的功能,計算機越多,CPU、內存、存儲資源也就越多,能夠處理的併發訪問和數據量就越大,進而能夠爲更多的用戶提供服務。

但分佈式在解決網站高併發問題的同時也帶來了其他問題。首先,分佈式意味着服務調用必須通過網絡,這可能會對性能造成比較嚴重的影響;其次,服務器越多,服務器宕機的概率也就越大,一臺服務器宕機造成的服務不可用可能會導致很多應用不可訪問,使網站可用性降低;另外,數據在分佈式的環境中保持數據一致性也非常困難,分佈式事務也難以保證,這對網站業務正確性和業務流程有可能造成很大影響;分佈式還導致網站依賴錯綜複雜,開發管理維護困難。因此分佈式設計要根據具體情況量力而行,切莫爲了分佈式而分佈式。

分佈式應用和服務。

分佈式靜態資源。

即人們常說的動靜分離。靜態資源分佈式部署可以減輕應用服務器的負載壓力;通過使用獨立域名加快瀏覽器併發加載的速度;由負責用戶體驗的團隊進行開發維護有利於網站分工合作,使不同技術工種術業有專攻。

分佈式數據和存儲。

分佈式計算。

目前網站普遍使用Hadoop及其MapReduce分佈式計算框架進行此類批處理計算,其特點是移動計算而不是移動數據,將計算程序分發到數據所在的位置以加速計算和分佈式計算。

使用分佈式雖然已經將分層和分割後的模塊獨立部署,但是對於用戶訪問集中的模塊(比如網站的首頁),還需要將獨立部署的服務器集羣化,即多臺服務器部署相同應用構成一個集羣,通過負載均衡設備共同對外提供服務。

所以在網站應用中,即使是訪問量很小的分佈式應用和服務,也至少要部署兩臺服務器構成一個小的集羣,目的就是提高系統的可用性。

緩存就是將數據存放在距離計算最近的位置以加快處理速度。緩存是改善軟件性能的第一手段,現代CPU越來越快的一個重要因素就是使用了更多的緩存,在複雜的軟件設計中,緩存幾乎無處不在。大型網站架構設計在很多方面都使用了緩存設計。

CDN:即內容分發網絡,部署在距離終端用戶最近的網絡服務商,用戶的網絡請求總是先到達他的網絡服務商那裏,在這裏緩存網站的一些靜態資源(較少變化的數據),可以就近以最快速度返回給用戶,如視頻網站和門戶網站會將用戶訪問量大的熱點內容緩存在CDN。

反向代理:反向代理屬於網站前端架構的一部分,部署在網站的前端,當用戶請求到達網站的數據中心時,最先訪問到的就是反向代理服務器,這裏緩存網站的靜態資源,無需將請求繼續轉發給應用服務器就能返回給用戶。

本地緩存:在應用服務器本地緩存着熱點數據,應用程序可以在本機內存中直接訪問數據,而無需訪問數據庫。

分佈式緩存:大型網站的數據量非常龐大,即使只緩存一小部分,需要的內存空間也不是單機能承受的,所以除了本地緩存,還需要分佈式緩存,將數據緩存在一個專門的分佈式緩存集羣中,應用程序通過網絡通信訪問緩存數據。

使用緩存有兩個前提條件,一是數據訪問熱點不均衡,某些數據會被更頻繁的訪問,這些數據應該放在緩存中;二是數據在某個時間段內有效,不會很快過期,否則緩存的數據就會因已經失效而產生髒讀,影響結果的正確性。

網站數據庫幾乎都是按照有緩存的前提進行負載能力設計的。

降低軟件耦合性

直接關係越少,就越少被彼此影響,越可以獨立發展。

在單一服務器內部可通過多線程共享內存隊列的方式實現異步,處在業務操作前面的線程將輸出寫入到隊列,後面的線程從隊列中讀取數據進行處理;在分佈式系統中,多個服務器集羣通過分佈式消息隊列實現異步,分佈式消息隊列可以看作內存隊列的分佈式部署。

提高系統可用性。消費者服務器發生故障,數據會在消息隊列服務器中存儲堆積,生產者服務器可以繼續處理業務請求,系統整體表現無故障。消費者服務器恢復正常後,繼續處理消息隊列中的數據。

加快網站響應速度。處在業務處理前端的生產者服務器在處理完業務請求後,將數據寫入消息隊列,不需要等待消費者服務器處理就可以返回,響應延遲減少。

消除併發訪問高峯。

使用消息隊列將突然增加的訪問請求數據放入消息隊列中,等待消費者服務器依次處理,就不會對整個網站負載造成太大壓力。

使用異步方式處理業務可能會對用戶體驗、業務流程造成影響。

目前大型網站的自動化架構設計主要集中在發佈運維方面。

發佈過程包括諸多環節。自動化代碼管理,代碼版本控制、代碼分支創建合併等過程自動化,開發工程師只要提交自己參與開發的產品代號,系統就會自動爲其創建開發分支,後期會自動進行代碼合併;自動化測試,代碼開發完成,提交測試後,系統自動將代碼部署到測試環境,啓動自動化測試用例進行測試,向相關人員發送測試報告,向系統反饋測試結果;自動化安全檢測,安全檢測工具通過對代碼進行靜態安全掃描及部署到安全測試環境進行安全×××測試,評估其安全性;最後進行自動化部署,將工程代碼自動部署到線上生產環境。

檢測到故障發生後,系統會進行自動化失效轉移,將失效的服務器從集羣中隔離出去,不再處理系統中的應用請求。待故障消除後,系統進行自動化失效恢復,重新啓動服務,同步數據保證數據的一致性。在網站遇到訪問高峯,超出網站最大處理能力時,爲了保證整個網站的安全可用,還會進行自動化降級,通過拒絕部分請求及關閉部分不重要的服務將系統負載降至一個安全的水平,必要時,還需要自動化分配資源,將空閒資源分配給重要的服務,擴大其部署規模。

2.3 小結

模式受其適用場景限制,對系統的要求和約束也很多,不恰當地使用模式只會畫虎不成反類犬,不但沒有解決原來的老問題,反而帶來了更棘手的新問題。

好的設計絕對不是模仿,不是生搬硬套某個模式,而是對問題深刻理解之上的創造與創新

山寨與創新的最大區別不在於是否抄襲,是否模仿,而在於對問題和需求是否真正理解與把握。

3 大型網站核心架構要素

關於什麼是架構,一種比較通俗的說法是“最高層次的規劃,難以改變的決定”,這些規劃和決定奠定了事物未來發展的方向和最終的藍圖。

除了當前的系統功能需求外,軟件架構還需要關注性能、可用性、伸縮性、擴展性和安全性這5個架構要素,架構設計過程中需要平衡這5個要素之間的關係以實現需求和架構目標,也可以通過考察這些架構要素來衡量一個軟件架構設計的優劣,判斷其是否滿足期望。

3.1 性能

在代碼層面,也可以通過使用多線程、改善內存管理等手段優化性能。

對於網站而言,性能符合預期僅僅是必要條件,因爲無法預知網站可能會面臨的訪問壓力,所以必須要考察系統在高併發訪問情況下,超出負載設計能力的情況下可能會出現的性能問題。網站需要長時間持續運行,還必須保證系統在持續運行且訪問壓力不均勻的情況下保持穩定的性能特性。

3.2 可用性

影響網站聲譽,重則可能會攤上官司。對於電子商務類網站,網站不可用還意味着損失金錢和用戶。因此幾乎所有網站都承諾7.24可用,但事實上任何網站都不可能達到完全的7.24可用,總會有一些故障時間,扣除這些故障時間,就是網站的總可用時間,這個時間可以換算成網站的可用性指標,以此衡量網站的可用性,一些知名大型網站可以做到4個9以上的可用性,也就是可用性超過99.99%。

網站高可用的主要手段是冗餘,應用部署在多臺服務器上同時提供訪問,數據存儲在多臺服務器上互相備份,任何一臺服務器宕機都不會影響應用的整體可用,也不會導致數據丟失。

但是一個前提條件是應用服務器上不能保存請求的會話信息。否則服務器宕機,會話丟失,即使將用戶請求轉發到其他服務器上也無法完成業務處理。

3.3 伸縮性

所謂伸縮性是指通過不斷向集羣中加入服務器的手段來緩解不斷上升的用戶併發訪問壓力和不斷增長的數據存儲需求。

衡量架構伸縮性的主要標準就是是否可以用多臺服務器構建集羣,是否容易向集羣中添加新的服務器。加入新的服務器後是否可以提供和原來的服務器無差別的服務。集羣中可容納的總的服務器數量是否有限制。

對於應用服務器集羣,只要服務器上不保存數據,所有服務器都是對等的,通過使用合適的負載均衡設備就可以向集羣中不斷加入服務器。

對於緩存服務器集羣,加入新的服務器可能會導致緩存路由失效。

需要改進緩存路由算法保證緩存數據的可訪問性。

關係數據庫雖然支持數據複製,主從熱備等機制,但是很難做到大規模集羣的可伸縮性,因此關係數據庫的集羣伸縮性方案必須在數據庫之外實現,通過路由分區等手段將部署有多個數據庫的服務器組成一個集羣。

至於大部分NoSQL數據庫產品,由於其先天就是爲海量數據而生,因此其對伸縮性的支持通常都非常好,可以做到在較少運維參與的情況下實現集羣規模的線性伸縮。

3.4 擴展性

衡量網站架構擴展性好壞的主要標準就是在網站增加新的業務產品時,是否可以實現對現有產品透明無影響,不需要任何改動或者很少改動既有業務功能就可以上線新產品。不同產品之間是否很少耦合,一個產品改動對其他產品無影響,其他產品和功能不需要受牽連進行改動。

事件驅動架構在網站通常利用消息隊列實現,將用戶請求和其他業務事件構造成消息發佈到消息隊列,消息的處理者作爲消費者從消息隊列中獲取消息進行處理。通過這種方式將消息產生和消息處理分離開來,可以透明地增加新的消息生產者任務或者新的消息消費者任務。

分佈式服務則是將業務和可複用服務分離開來,通過分佈式服務框架調用。新增產品可以通過調用可複用的服務實現自身的業務邏輯,而對現有產品沒有任何影響。可複用服務升級變更的時候,也可以通過提供多版本服務對應用實現透明升級,不需要強制應用同步變更。

4 瞬時響應:網站的高性能架構

同類型的兩個網站,X網站服務器平均每個請求的處理時間是500毫秒,Y網站服務器平均每個請求的處理時間是1000毫秒,爲什麼用戶卻反映Y網站的速度快呢?

網站性能是客觀的指標,可以具體體現到響應時間、吞吐量等技術指標,同時也是主觀的感受,而感受則是一種與具體參與者相關的微妙的東西,用戶的感受和工程師的感受不同,不同的用戶感受也不同。

4.1 網站性能測試

軟件工程師說到網站性能的時候,通常和用戶說的不一樣。

從用戶角度,網站性能就是用戶在瀏覽器上直觀感受到的網站響應速度快還是慢。

在實踐中,使用一些前端架構優化手段,通過優化頁面HTML式樣、利用瀏覽器端的併發和異步特性、調整瀏覽器緩存策略、使用CDN服務、反向代理等手段,使瀏覽器儘快地顯示用戶感興趣的內容、儘可能近地獲取頁面內容,即使不優化應用程序和架構,也可以很大程度地改善用戶視角下的網站性能。

開發人員關注的主要是應用程序本身及其相關子系統的性能,包括響應延遲、系統吞吐量、併發處理能力、系統穩定性等技術指標。

運維人員更關注基礎設施性能和資源利用率,如網絡運營商的帶寬能力、服務器硬件的配置、數據中心網絡架構、服務器和網絡帶寬的資源利用率等。

不同視角下有不同的性能標準,不同的標準有不同的性能測試指標,從開發和測試人員的視角,網站性能測試的主要指標有響應時間、併發數、吞吐量、性能計數器等。

指應用執行一個操作需要的時間,包括從發出請求開始到收到最後響應數據所需要的時間。響應時間是系統最重要的性能指標,直觀地反映了系統的“快慢“。

指系統能夠同時處理請求的數目,這個數字也反映了系統的負載特性。對於網站而言,併發數即網站併發用戶數,指同時提交請求的用戶數。

用戶瀏覽器顯示“Service is too busy”而告終。出現這種情況,有可能是網站技術準備不充分導致,也有可能是運營人員錯誤地評估併發用戶數導致。

測試程序並不是啓動多線程然後不停地發送請求,而是在兩次請求之間加入一個隨機等待時間,這個時間被稱作思考時間。

TPS(每秒事務數)是吞吐量的一個常用量化指標,此外還有HPS(每秒HTTP請求數)、QPS(每秒查詢數)等。

系統吞吐量和系統併發數,以及響應時間的關係可以形象地理解爲高速公路的通行狀況:吞吐量是每天通過收費站的車輛數目(可以換算成收費站收取的高速費),併發數是高速公路上的正在行駛的車輛數目,響應時間是車速。車輛很少時,車速很快,但是收到的高速費也相應較少;隨着高速公路上車輛數目的增多,車速略受影響,但是收到的高速費增加很快;隨着車輛的繼續增加,車速變得越來越慢,高速公路越來越堵,收費不增反降;如果車流量繼續增加,超過某個極限後,任何偶然因素都會導致高速全部癱瘓,車走不動,費當然也收不着,而高速公路成了停車場(資源耗盡)。

System Load即系統負載,指當前正在被CPU執行和等待被CPU執行的進程數目總和,是反映系統忙閒程度的重要指標。多核CPU的情況下,完美情況是所有CPU都在使用,沒有進程在等待處理,所以Load的理想值是CPU的數目。當Load值低於CPU數目的時候,表示CPU有空閒,資源存在浪費;當Load值高於CPU數目的時候,表示進程在排隊等待CPU調度,表示系統資源不足,影響應用程序的執行性能。

性能測試是一個總稱,具體可細分爲性能測試、負載測試、壓力測試、穩定性測試。

隨着壓力的持續增加,系統處理能力增加變緩,直到達到一個最大值(c點),這是系統的最大負載點,這一段被稱作負載測試。測試目標是評估當系統因爲突發事件超出日常訪問壓力的情況下,保證系統正常運行情況下能夠承受的最大訪問負載壓力;超過這個點後,再增加壓力,系統的處理能力反而下降,而資源消耗卻更多,直到資源消耗達到極限(d點),這個點可以看作是系統的崩潰點,超過這個點繼續加大併發請求數目,系統不能再處理任何請求,這一段被稱作壓力測試,測試目標是評估可能導致系統崩潰的最大訪問負載壓力。

與性能曲線相對應的是用戶訪問的等待時間(系統響應時間)。

如果性能測試結果不能滿足設計或業務需求,那麼就需要尋找系統瓶頸,分而治之,逐步優化。

4.2 Web前端性能優化

HTTP協議是無狀態的應用層協議,意味着每次HTTP請求都需要建立通信鏈路、進行數據傳輸,而在服務器端,每個HTTP都需要啓動獨立的線程去處理。

減少HTTP的主要手段是合併CSS、合併JavaScript、合併圖片。

靜態資源文件更新的頻率都比較低,而這些文件又幾乎是每次HTTP請求都需要的,如果將這些文件緩存在瀏覽器中,可以極好地改善性能。

在某些時候,靜態資源文件變化需要及時應用到客戶端瀏覽器,這種情況,可通過改變文件名實現,在服務器端對文件進行壓縮,在瀏覽器端對文件解壓縮,可有效減少通信傳輸的數據量。

但是壓縮對服務器和瀏覽器產生一定的壓力,在通信帶寬良好,而服務器資源不足的情況下要權衡考慮。

瀏覽器會在下載完全部CSS之後纔對整個頁面進行渲染,因此最好的做法是將CSS放在頁面最上面,讓瀏覽器儘快下載CSS。

對於某些靜態資源的訪問,如CSS、Script等,發送Cookie沒有意義,可以考慮靜態資源使用獨立域名訪問,避免請求靜態資源時發送Cookie,減少Cookie傳輸的次數。

CDN能夠緩存的一般是靜態資源。

而反向代理服務器位於網站機房一側,代理網站Web服務器接收HTTP請求。

來自互聯網的訪問請求必須經過代理服務器,相當於在Web服務器和可能的網絡×××之間建立了一個屏障。

除了安全功能,代理服務器也可以通過配置緩存功能加速Web請求。

反向代理也可以實現負載均衡的功能,而通過負載均衡構建的應用集羣可以提高系統總體處理能力,進而改善網站高併發情況下的性能。

4.3 應用服務器性能優化

網站性能優化第一定律:優先考慮使用緩存優化性能。

速度的存儲介質中,以供系統處理。一方面緩存訪問速度快,可以減少數據訪問的時間,另一方面如果緩存的數據是經過計算處理得到的,那麼被緩存的數據無需重複計算即可直接使用,因此緩存還起到減少計算時間的作用。

緩存主要用來存放那些讀寫比很高、很少變化的數據,如商品的類目信息,熱門詞的搜索列表信息,熱門商品信息等。應用程序讀取數據時,先到緩存中讀取,如果讀取不到或數據已失效,再訪問數據庫,並將數據寫入緩存。

網站數據訪問通常遵循二八定律,即80%的訪問落在20%的數據上,因此利用Hash表和內存的高速訪問特性,將這20%的數據緩存起來,可很好地改善系統性能,提高數據讀取速度,降低存儲訪問壓力。

如果緩存中保存的是頻繁修改的數據,就會出現數據寫入緩存後,應用還來不及讀取緩存,數據就已失效的情形,徒增系統負擔。一般說來,數據的讀寫比在2:1以上,即寫入一次緩存,在數據更新前至少讀取兩次,緩存纔有意義。

如果應用系統訪問數據沒有熱點,不遵循二八定律,即大部分數據訪問並沒有集中在小部分數據上,那麼緩存就沒有意義,因爲大部分數據還沒有被再次訪問就已經被擠出緩存了。

當緩存服務崩潰時,數據庫會因爲完全不能承受如此大的壓力而宕機,進而導致整個網站不可用。這種情況被稱作緩存雪崩,發生這種故障,甚至不能簡單地重啓緩存服務器和數據庫服務器來恢復網站訪問。

有的網站通過緩存熱備等手段提高緩存可用性:當某臺緩存服務器宕機時,將緩存訪問切換到熱備服務器上。但是這種設計顯然有違緩存的初衷,緩存根本就不應該被當做一個可靠的數據源來使用。

通過分佈式緩存服務器集羣,將緩存數據分佈到集羣多臺服務器上可在一定程度上改善緩存的可用性。當一臺緩存服務器宕機的時候,只有部分緩存數據丟失

產品在設計之初就需要一個明確的定位:什麼是產品要實現的功能,什麼不是產品提供的特性。在產品漫長的生命週期中,會有形形×××的困難和誘惑來改變產品的發展方向,左右搖擺、什麼都想做的產品,最後有可能成爲一個失去生命力的四不像。

緩存穿透

如果因爲不恰當的業務、或者惡意×××持續高併發地請求某個不存在的數據,由於緩存沒有保存該數據,所有的請求都會落到數據庫上,會對數據庫造成很大壓力,甚至崩潰。一個簡單的對策是將不存在的數據也緩存起來(其value值爲null)。

JBoss Cache的分佈式緩存在集羣中所有服務器中保存相同的緩存數據,當某臺服務器有緩存數據更新的時候,會通知集羣中其他機器更新緩存數據或清除緩存數據帶來的問題是緩存數據的數量受限於單一服務器的內存空間,而且當集羣規模較大的時候,緩存更新信息需要同步到集羣所有機器,其代價驚人。因而這種方案更多見於企業應用系統中,而很少在大型網站使用。

Memcached採用一種集中式的緩存集羣管理,也被稱作互不通信的分佈式架構方式。緩存與應用分離部署,緩存系統部署在一組專門的服務器上,應用程序通過一致性Hash等路由算法選擇緩存服務器遠程訪問緩存數據,緩存服務器之間不通信,緩存集羣的規模可以很容易地實現擴容,具有良好的可伸縮性。

Memcached以後,許多NoSQL產品都借鑑了或直接支持這套協議。

正是集羣內服務器互不通信使得集羣可以做到幾乎無限制的線性伸縮,這也正是目前流行的許多大數據技術在不使用消息隊列的情況下,用戶的請求數據直接寫入數據庫,在高併發的情況下,會對數據庫造成巨大的壓力,同時也使得響應延遲加劇。在使用消息隊列後,用戶請求的數據發送給消息隊列後立即返回,再由消息隊列的消費者進程(通常情況下,該進程通常獨立部署在專門的服務器集羣上)從消息隊列中獲取數據,異步寫入數據庫。

消息隊列具有很好的削峯作用——即通過異步處理,將短時間高併發產生的事務消息存儲在消息隊列中,從而削平高峯期的併發事務。

任何可以晚點做的事情都應該晚點再做。

多線程編程一個需要注意的問題是線程安全問題,即多線程併發對某個資源進行修改,導致數據混亂。這也是缺乏經驗的網站工程師最容易犯錯的地方,而線程安全Bug又難以測試和重現,網站故障中,許多所謂偶然發生的“靈異事件”都和多線程併發問題有關

頻繁創建關閉數據庫連接,對數據庫服務器而言是災難性的,同時頻繁創建關閉連接也需要花費較長的時間。因此在實踐中,應用程序的數據庫連接基本都使用連接池(Connection Pool)的方式。數據庫連接對象創建好以後,將連接對象放入對象池容器中,應用程序要連接的時候,就從對象池中獲取一個空閒的連接使用,使用完畢再將該對象歸還到對象池中即可,不需要創建新的連接。

這些所謂的連接池、線程池,本質上都是對象池,即連接、線程都是對象,池管理方式也基本相同。

如果Web應用運行在JVM等具有垃圾回收功能的環境中,那麼垃圾回收可能會對系統的性能特性產生巨大影響。理解垃圾回收機制有助於程序優化和參數調優,以及編寫內存安全的代碼。

4.4 存儲性能優化

海量的數據讀寫對磁盤訪問造成巨大壓力,雖然可以通過Cache解決一部分數據讀壓力,但是很多時候,磁盤仍然是系統最嚴重的瓶頸。而且磁盤中存儲的數據是網站最重要的資產,磁盤的可用性和容錯性也至關重要。

爲了改善數據訪問特性,文件系統或數據庫系統通常會對數據排序後存儲,加快數據檢索速度,這就需要保證數據在不斷更新、插入、刪除後依然有序,傳統關係數據庫的做法是使用B+樹樹的層次最多三層。因此可能需要5次磁盤訪問才能更新一條記錄(三次磁盤訪問獲得數據索引及行ID,然後再進行一次數據文件讀操作及一次數據文件寫操作)。

但是由於每次磁盤訪問都是隨機的,而傳統機械硬盤在數據隨機訪問時性能較差,每次數據訪問都需要多次訪問磁盤影響數據訪問性能。

目前許多NoSQL產品採用LSM樹作爲主要數據結構

在LSM樹上進行一次數據更新不需要磁盤訪問,在內存即可完成,速度遠快於B+樹。當數據訪問以寫操作爲主,而讀操作則集中在最近寫入的數據上時,使用LSM樹可以極大程度地減少磁盤的訪問次數,加快訪問速度。

作爲存儲結構,B+樹不是關係數據庫所獨有的,NoSQL數據庫也可以使用B+樹。同理,關係數據庫也可以使用LSM,而且隨着SSD硬盤的日趨成熟及大容量持久存儲的內存技術的出現,相信B+樹這一“古老”的存儲結構會再次煥發青春。

但是在數據修改較多的場景中,修改任何磁盤數據都會導致第N塊磁盤重寫校驗數據,頻繁寫入的後果是第N塊磁盤比其他磁盤容易損壞,需要頻繁更換,所以RAID3很少在實

踐中使用。

RAID5和RAID3很相似,但是校驗數據不是寫入第N塊磁盤,而是螺旋式地寫入所有磁盤中。這樣校驗數據的修改也被平均到所有磁盤上,避免RAID3頻繁寫壞一塊磁盤的情況。

RAID6和RAID5類似,但是數據只寫入N.2塊磁盤,並螺旋式地在兩塊磁盤中寫入校驗信息(使用不同算法生成)。

RAID技術在傳統關係數據庫及文件系統中應用比較廣泛,但是在大型網站比較喜歡使用的NoSQL,以及分佈式文件系統中,RAID技術卻遭到冷落。

例如在HDFS(Hadoop分佈式文件系統)中,系統在整個存儲集羣的多臺服務器上進行數據併發讀寫和備份,可以看作在服務器集羣規模上實現了類似RAID的功能,因此不需要磁盤RAID。

HDFS以塊(Block)爲單位管理文件內容,一個文件被分割成若干個Block,當應用程序寫文件時,每寫完一個Block,HDFS就將其自動複製到另外兩臺機器上,保證每個Block有三個副本,即使有兩臺服務器宕機,數據依然可以訪問,相當於實現了RAID1的數據複製功能。

當對文件進行處理計算時,通過MapReduce併發計算任務框架,可以啓動多個計算子任務(MapReduce Task),同時讀取文件的多個Block,併發處理,相當於實現了RAID0的併發訪問功能。

和操作系統一樣,HDFS對數據存儲空間的管理以數據塊(Block)爲單位,只是比操作系統中的數據塊(512字節)要大得多,默認爲64MB。HDFS將DataNode上的磁盤空間分成N個這樣的塊,供應用程序使用。

當Client寫完一個數據塊時,HDFS會將這個數據塊再複製兩份存儲在其他DataNode服務器上,HDFS默認同一份數據有三個副本,保證數據可靠性。因此在HDFS中,即使DataNode服務器有多塊磁盤,也不需要使用RAID進行數據備份,而是在整個集羣上進行數據複製,而且系統一旦發現某臺服務器宕機,會自動利用其他機器上的數據將這臺服務器上存儲的數據塊自動再備份一份,從而獲得更高的數據可靠性。

4.5 小結

最終設計出來的可能是B方案:縮短高併發訪問響應延遲的同時,卻延長了原來低併發訪問時的響應延遲。架構師對這種可能性要心中有數,合理調整相關各方對性能優化的心理預期。

歸根結底,技術是爲業務服務的,技術選型和架構決策依賴業務規劃乃至企業戰略規劃,離開業務發展的支撐和驅動,技術走不遠,甚至還會迷路。

5 萬無一失:網站的高可用架構

網站的可用性(Availability)描述網站可有效訪問的特性(不同於另一個網站運營指標:Usability,通常也被譯作可用性,但是後者強調的是網站的有用性,即對最終用戶的使用價值),相比於網站的其他非功能特性,網站的可用性更牽動人們的神經。

5.1 網站可用性的度量與考覈

網站不可用也被稱作網站故障,業界通常用多少個9來衡量網站的可用性,如QQ的可用性是4個9,即QQ服務99.99%可用,這意味着QQ服務要保證其在所有運行時間中,只有0.01%的時間不可用,也就是一年中大約最多53分鐘不可用。

網站不可用時間(故障時間)=故障修復時間點-故障發現(報告)時間點

網站年度可用性指標=(1-網站不可用時間/年度總時間)×100%

由於可用性影響因素很多,對於網站整體而言,達到4個9,乃至5個9的可用性,除了過硬的技術、大量的設備資金投入和工程師的責任心,還要有個好運氣。

從管理層面,可用性指標是網站或者產品的整體考覈指標,具體到每個工程師的考覈,更多的是使用故障分。

故障分的計算公式爲:

故障分=故障時間(分鐘)×故障權重

有時候一個故障責任可能由多個部門或團隊來承擔,故障分也會相應按責任分攤到不同的團隊和個人。

不同的公司有不同的企業文化和市場策略,這些因素也會影響到系統可用性的架構決策,崇尚創新和風險的企業會對可用性要求稍低一些;業務快速增長的網站忙於應對指數級增長的用戶,也會降低可用性的標準;服務於收費用戶的網站則會比服務於免費用戶的網站對可用性更加敏感,服務不可用或關鍵用戶數據丟失可能會導致收費用戶的投訴甚至引來官司。

5.2 高可用的網站架構

典型的分層模型是三層,即應用層、服務層、數據層;各層之間具有相對獨立性,應用層主要負責具體業務邏輯處理;服務層負責提供可複用的服務;數據層負責數據的存儲與訪問。中小型網站在具體部署時,通常將應用層和服務層部署在一起,而數據層則另外部署。

網站的可用性架構設計不但要考慮實際的硬件故障引起的宕機,還要考慮網站升級發佈引起的宕機,而後者更加頻繁,不能因爲系統可以接受偶爾的停機故障就降低可用性設計的標準。

5.3 高可用的應用

應用層主要處理網站應用的業務邏輯,因此有時也稱作業務邏輯層,應用的一個顯著特點是應用的無狀態性。

所謂無狀態的應用是指應用服務器不保存業務的上下文信息,而僅根據每次請求提交的數據進行相應的業務邏輯處理,多個服務實例(服務器)之間完全對等,請求提交到任意服務器,處理結果都是完全一樣的。

由於負載均衡在應用層實際上起到了系統高可用的作用,因此即使某個應用訪問量非常少,只用一臺服務器提供服務就綽綽有餘,但如果需要保證該服務高可用,也必須至少部署兩臺服務器,使用負載均衡技術構建一個小型的集羣。

Web應用中將這些多次請求修改使用的上下文對象稱作會話(Session),單機情況下,Session可由部署在服務器上的Web容器(如JBoss)管理。在使用負載均衡的集羣環境

中,由於負載均衡服務器可能會將請求分發到集羣任何一臺應用服務器上,所以保證每次請求依然能夠獲得正確的Session比單機時要複雜很多。

集羣環境下,Session管理主要有以下幾種手段。

Session複製是早期企業應用系統使用較多的一種服務器集羣Session管理機制。

這種方案雖然簡單,從本機讀取Session信息也很快速,但只能使用在集羣規模比較小的情況下。當集羣規模較大時,集羣服務器間需要大量的通信進行Session複製,佔用服務器和網絡的大量資源,系統不堪負擔。

Session綁定可以利用負載均衡的源地址Hash算法實現,負載均衡服務器總是將來源於同一IP的請求分發到同一臺服務器上(也可以根據Cookie信息將同一個用戶的請求總是分發到同一臺服務器上,當然這時負載均衡服務器必須工作在HTTP協議層上。

但是Session綁定的方案顯然不符合我們對系統高可用的需求,因爲一旦某臺服務器宕機,那麼該機器上的Session也就不復存在了。

網站沒有客戶端,但是可以利用瀏覽器支持的Cookie記錄Session

利用Cookie記錄Session也有一些缺點,比如受Cookie大小限制,能記錄的信息有限;每次請求響應都需要傳輸Cookie,影響性能;如果用戶關閉Cookie,訪問就會不正常。因此事實上,許多網站都或多或少地使用Cookie記錄Session。

利用獨立部署的Session服務器(集羣)統一管理Session,應用服務器每次讀寫Session時,都訪問Session服務器,如圖5.9所示。

這種解決方案事實上是將應用服務器的狀態分離,分爲無狀態的應用服務器和有狀態的Session服務器,然後針對這兩種服務器的不同特性分別設計其架構。

5.4 高可用的服務

可複用的服務和應用一樣,也是無狀態的服務,因此可以使用類似負載均衡的失效轉移策略實現高可用的服務。

運維上將服務器進行分級管理,核心應用和服務優先使用更好的硬件,在運維響應速度上也格外迅速。

低優先級的服務通過啓動不同的線程或者部署在不同的虛擬機上進行隔離,而高優先級的服務則需要部署在不同的物理機上,核心服務和數據甚至需要部署在不同地域的數據中心。

在應用程序中設置服務調用的超時時間,一旦超時,通信框架就拋出異常,應用程序根據服務調度策略,可選擇繼續重試或將請求轉移到提供相同服務的其他服務器上。

如果採用異步調用的方式,應用程序將用戶註冊信息發送給消息隊列服務器後立即返回用戶註冊成功響應。而記錄用戶註冊信息到數據庫、發送用戶註冊成功郵件、調用用戶服務開通權限這三個服務作爲消息的消費者任務,分別從消息隊列獲取用戶註冊信息異步執行。

在網站訪問高峯期,服務可能因爲大量的併發調用而性能下降,嚴重時可能會導致服務宕機。爲了保證核心應用和功能的正常運行,需要對服務進行降級。降級有兩種手段:拒絕服務及關閉服務。

拒絕服務:拒絕低優先級應用的調用,減少服務調用併發數,確保核心應用正常使用;或者隨機拒絕部分請求調用,節約資源,讓另一部分請求得以成功,避免要死大家一起死的慘劇。

關閉功能:關閉部分不重要的服務,或者服務內部關閉部分不重要的功能,以節約系統開銷,爲重要的服務和功能讓出資源

服務重複調用是無法避免的,應用層也不需要關心服務是否真的失敗,只要沒有收到調用成功的響應,就可以認爲調用失敗,並重試服務調用。因此必須在服務層保證服務重複調用和調用一次產生的結果相同,即服務具有冪等性。

5.5 高可用的數據

保證數據存儲高可用的手段主要是數據備份和失效轉移機制。數據備份是保證數據有多個副本,任意副本的失效都不會導致數據的永久丟失,從而實現數據完全的持久化。而失效轉移機制則保證當一個數據副本不可訪問時,可以快速切換訪問數據的其他副本,保證系統可用。

另一種觀點認爲,緩存服務不是數據存儲服務,緩存服務器宕機引起緩存數據丟失導致服務器負載壓力過高應該通過其他手段解決,而不是提高緩存服務本身的高可用。

筆者持後一種觀點,對於緩存服務器集羣中的單機宕機,如果緩存服務器集羣規模較大,那麼單機宕機引起的緩存數據丟失比例和數據庫負載壓力變化都較小,對整個系統影響也較小。擴大緩存服務器集羣規模的一個簡單手段就是整個網站共享同一個分佈式緩存集羣,單獨的應用和產品不需要部署自己的緩存服務器,只需要向共享緩存集羣申請緩存資源即可。並且通過邏輯或物理分區的方式將每個應用的緩存部署在多臺服務器上,任何一臺服務器宕機引起的緩存失效都隻影響應用緩存數據的一小部分,不會對應用性能和數據庫負載造成太大影響。

數據持久性

數據可訪問性

數據一致性

CAP原理認爲,一個提供數據服務的存儲系統無法同時滿足數據一致(Consistency)、數據可用性(Availibility)、分區耐受性(Patition Tolerance,系統具有跨網絡分區的伸縮性)這三個條件。

所以在大型網站中,通常會選擇強化分佈式存儲系統的可用性。

(A)和伸縮性(P),而在某種程度上放棄一致性(C)。

CAP原理對於可伸縮的分佈式系統設計具有重要意義,在系統設計開發過程中,不恰當地迎合各種需求,企圖打造一個完美的產品,可能會使設計進入兩難境地,難以爲繼。

數據強一致

各個副本的數據在物理存儲中總是一致的;數據更新操作結果和操作響應總是一致的,即操作響應通知更新失敗,那麼數據一定沒有被更新,而不是處於不確定狀態。

數據用戶一致

即數據在物理存儲中的各個副本的數據可能是不一致的,但是終端用

戶訪問時,通過糾錯和校驗機制,可以確定一個一致的且正確的數據返回給用戶。

數據最終一致

這是數據一致性中較弱的一種,即物理存儲的數據可能是不一致的,終端用戶訪問到的數據可能也是不一致的(同一用戶連續訪問,結果不同;或者不同用戶同時訪問,結果不同),但系統經過一段時間(通常是一個比較短的時間段)的自我恢復和修正,數據最終會達到一致。

因此,數據冷備作爲一種傳統的數據保護手段,依然在網站日常運維中使用,同時在網站實時在線業務中,還需要進行數據熱備,以提供更好的數據可用性。

數據熱備可分爲兩種:異步熱備方式和同步熱備方式。

同步熱備具體實現的時候,爲了提高性能,在應用程序客戶端併發向多個存儲服務器同時寫入數據,然後等待所有存儲服務器都返回操作成功的響應後,再通知應用程序寫操作成功。

這種情況下,存儲服務器沒有主從之分,完全對等,更便於管理和維護。存儲服務客戶端在寫多份數據的時候,併發操作,這意味着多份數據的總寫操作延遲是響應最慢的那臺存儲服務器的響應延遲,而不是多臺存儲服務器響應延遲之和。其性能和異步熱備方式差不多。

關係數據庫熱備機制就是通常所說的Master-Slave同步機制。Master-Slave機制不但解決了數據備份問題,還改善了數據庫系統的性能,實踐中,通常使用讀寫分離的方法訪問Slave和Master數據庫,寫操作只訪問Master數據庫,讀操作只訪問Slave數據庫。

失效轉移操作由三部分組成:失效確認、訪問轉移、數據恢復。

是否宕機的手段有兩種:心跳檢測和應用程序訪問失敗報告。

5.6 高可用網站的軟件質量保證

不管發佈的新功能是修改了一個按鈕的佈局還是增加了一個核心業務,都需要在服務器上關閉原有的應用,然後重新部署啓動新的應用,整個過程還要求不影響用戶的使用。這相當於要求給飛行中的飛機換個引擎,既不能讓飛機有劇烈晃動(影響用戶體驗),也不能讓飛機降落(系統停機維護),更不能讓飛機墜毀(系統故障網站完全不可用)。

如果使用人工測試,成本、時間及測試覆蓋率都難以接受。

目前大部分網站都採用Web自動化測試技術,使用自動測試工具或腳本完成測試。

大型網站通常也會開發自己的自動化測試工具,可以一鍵完成系統部署,測試數據生成、測試執行、測試報告生成等全部測試過程。許多網站測試工程師的編碼能力毫不遜於軟件工程師。

因此在網站發佈時,並不是把測試通過的代碼包直接發佈到線上服務器,而是先發布到預發佈機器上,開發工程師和測試工程師在預發佈服務器上進行預發佈驗證,執行一些典型的業務流程,確認系統沒有問題後才正式發佈。

預發佈服務器是一種特殊用途的服務器,它和線上的正式服務器唯一的不同就是沒有配置在負載均衡服務器上,外部用戶無法訪問。

此外,在網站應用中強調的一個處理錯誤的理念是快速失敗(fast failed),即如果系統在啓動時發現問題就立刻拋出異常,停止啓動讓工程師介入排查錯誤,而不是啓動後執行錯誤的操作。

網站代碼控制的核心問題是如何進行代碼管理,既能保證代碼發佈版本的穩定正確,同時又能保證不同團隊的開發互不影響。

SVN代碼控制和版本發佈方式一般有以下兩種。

1. 主幹開發、分支發佈

代碼修改都在主幹(trunk)上進行,需要發佈的時候,從主幹上拉一個分支(branch)發佈,該分支即成爲一個發佈版本,如果該版本發現Bug,繼續在該分支上修改發佈,並將修改合併(merge)回主幹,直到下次主幹發佈。

2. 分支開發,主幹發佈

任何修改都不得在主幹上直接進行,需要開發一個新功能或者修復一個Bug時,從主幹拉一個分支進行開發,開發完成且測試通過後,合併回主幹,然後從主幹進行發佈,主幹上的代碼永遠是最新發布的版本。

目前網站應用開發中主要使用的是分支開發、主幹發佈的方而使用分支開發、主幹發佈的方式,只需要將A項目的分支合併回主幹即可發佈,不受B項目發佈時間的影響。

Git作爲版本控制工具,正逐步取代SVN的地位。

火車發佈模型:將每個應用的發佈過程看作一次火車旅程,火車定點運行,期間有若干站點,每一站都進行例行檢查,不通過的項目下車,剩下的項目繼續坐着火車旅行,直到火車到達終點(應用發佈成功)。但實際中,有可能所有項目都下車了,開着空車前進是沒有意義的,火車不得不回到起點,等待解決了問題再重來一次。還有可能是車上有達官貴人(重點項目,CEO跟投資人拍胸脯的項目),他不上車,誰也別想走,他出了錯,大家都跟着回去重來。

由於火車發佈模型是基於規則驅動的流程,所以這個流程可以自動化。採用火車發佈模型的網站會開發一個自動化發佈的工具實現發佈過程的自動化。

的干預越少,自動化程度越高,引入故障的可能性就越小,火車準點到達,大家按時下班的可能性就越大。

大型網站會使用灰度發佈模式,將集羣服務器分成若干部分,每天只發布一部分服務器,觀察運行穩定沒有故障,第二天繼續發佈一部分服務器,持續幾天才把整個集羣全部發布完畢,期間如果發現問題,只需要回滾已發佈的一部分服務器即可。

灰度發佈也常用於用戶測試,即在部分服務器上發佈新版本,其餘服務器保持老版本(或者發佈另一個版本),然後監控用戶操作行爲,收集用戶體驗報告,比較用戶對兩個版本的滿意度,以確定最終的發佈版本。這種手段也被稱作AB測試。

5.7 網站運行監控

運維沒有監控的網站,猶如駕駛沒有儀表的飛機

用戶行爲日誌指用戶在瀏覽器上所做的所有操作及其所在的操作環境,包括用戶操作系統與瀏覽器版本信息,IP地址、頁面訪問路徑、頁面停留時間等,這些數據對統計網站PV/UV指標、分析用戶行爲、優化網站設計、個性化營銷與推薦等非常重要。

服務器端日誌收集。

其缺點是可能會出現信息失真,如IP地址是代理服務器地址而不是用戶真實IP;無法識別訪問路徑等。

客戶端瀏覽器日誌收集。

其缺點是比較麻煩,需要在頁面嵌入特定的JavaScript腳本來完成。

大型網站的用戶日誌數據量驚人,數據存儲與計算壓力很大,目前許多網站逐步開發基於實時計算框架Storm的日誌統計與分析工具。

目前網站使用比較廣泛的開源性能監控工具是Ganglia,它支持大規模服務器集羣,並支持以圖形的方式在瀏覽器展示實時性能曲線。

監控系統還可以在發現故障的情況下主動通知應用,進行失效轉移。

優雅降級是指網站爲了應付突然爆發的訪問高峯,主動關閉部分功能,釋放部分系統資源,保證網站核心功能正常訪問的一個手段。

網站在監控管理基礎之上實現自動優雅降級,是網站柔性架構的理想狀態:監控系統實時監控所有服務器的運行狀況,根據監控參數判斷應用訪問負載情況,如果發現部分應用負載過高,而部分應用負載過低,就會適當卸載低負載應用部分服務器,重新安裝啓動部分高負載應用,使應用負載總體均衡,如果所有應用負載都很高,而且負載壓力還在繼續增加,就會自動關閉部分非重要功能,保證核心功能正常運行。

6 永無止境:網站的伸縮性架構

所謂網站的伸縮性是指不需要改變網站的軟硬件設計,僅僅通過改變部署的服務器數量就可以擴大或者縮小網站的服務處理能力。

大型網站不是一開始就是大型網站的,而是從小型網站逐步演化而來的,Google誕生的時候也才只有一臺服務器。設計一個大型網站或者一個大型軟件系統,和將一個小網站逐漸演化成一個大型網站,其技術方案是完全不同的。前者如傳統的銀行系統,在設計之初就決定了系統的規模,如要服務的用戶數、要處理的交易數等,然後採購大型計算機等昂貴的設備,將軟件系統部署在上面,即成爲一個大型系統,有朝一日這個大型系統也不能滿足需求了,就花更多的錢打造一個更大型的系統。而網站一開始不可能規劃出自己的規模,也不可能有那麼多錢去開發一個大型系統,更不可能到了某個階段再重新打造一個系統,只能摸着石頭過河,從一臺廉價的PC服務器開始自己的大型系統演化之路。

遺憾的是,有些傳統企業將自己的管理模式和經營理念也照搬到互聯網領域——在技術方面的表現就是一開始就企圖打造一個大型網站。

6.1 網站架構的伸縮性設計

一般說來,網站的伸縮性設計可分成兩類,一類是根據功能進行物理分離實現伸縮,一類是單一功能通過集羣實現伸縮。前者是不同的服務器部署不同的服務,提供不同的功能;後者是集羣內的多臺服務器部署相同的服務,提供相同的功能。

縱向分離(分層後分離):將業務處理流程上的不同部分分離部署,實現系統伸縮

橫向分離(業務分割後分離):將不同的業務模塊分離部署,實現系統伸縮

橫向分離的粒度可以非常小,甚至可以一個關鍵網頁部署一個獨立服務,比如對於電商網站非常重要的產品詳情頁面,商鋪頁面,搜索列表頁面,每個頁面都可以獨立部署,專門維護。

將不同功能分離部署可以實現一定程度的伸縮性,但是隨着網站訪問量的逐步增加,即使分離到最小粒度的獨立部署,單一的服務器也不能滿足業務規模的要求。因此必須使用服務器集羣,即將相同服務部署在多臺服務器上構成一個集羣整體對外提供

當一頭牛拉不動車的時候,不要去尋找一頭更強壯的牛,而是用兩頭牛來拉車。

集羣伸縮性又可分爲應用服務器集羣伸縮性和數據服務器集羣伸縮性

而數據服務器集羣也可分爲緩存數據服務器集羣和存儲數據服務器集羣,這兩種集羣的伸縮性設計也不大相同。

6.2 應用服務器集羣的伸縮性設計

如果HTTP請求分發裝置可以感知或者可以配置集羣的服務器數量,可以及時發現集羣中新上線或下線的服務器,並能向新上線的服務器分發請求,停止向已下線的服務器分發請求,那麼就實現了應用服務器集羣的伸縮性。

這裏,這個HTTP請求分發裝置被稱作負載均衡服務器。

利用HTTP重定向協議實現負載均衡

HTTP重定向服務器是一臺普通的應用服務器,其唯一的功能就是根據用戶的HTTP請求計算一臺真實的Web服務器地址,並將該Web服務器地址寫入HTTP重定向響應中(響應狀態碼302)返回給用戶瀏覽器。

缺點是瀏覽器需要兩次請求服務器才能完成一次訪問,性能較差

使用HTTP302響應碼重定向,有可能使搜索引擎判斷爲SEO作弊,降低搜索排名。

這是利用DNS處理域名解析請求的同時進行負載均衡處理的一種方案

缺點,就是目前的DNS是多級解析,每一級DNS都可能緩存A記錄,當下線某臺服務器後,即使修改了DNS的A記錄,要使其生效也需要較長時間,這段時間,DNS依然會將域名解析到已經下線的服務器,導致用戶訪問失敗;而且DNS負載均衡的控制權在域名服務商那裏,網站無法對其做更多改善和更強大的管理。

大多數反向代理服務器同時提供負載均衡的功能,管理一組Web服務器,將請求根據負載均衡算法轉發到不同Web服務器上

由於反向代理服務器轉發請求在HTTP協議層面,因此也叫應用層負載均衡。其優點是和反向代理服務器功能集成在一起,部署簡單。缺點是反向代理服務器是所有請求和響應的中轉站,其性能可能會成爲瓶頸。

IP負載均衡

在網絡層通過修改請求目標地址進行負載均衡。

顧名思義,數據鏈路層負載均衡是指在通信協議的數據鏈路層修改mac地址進行負載均衡

使用三角傳輸模式的鏈路層負載均衡是目前大型網站使用最廣的一種負載均衡手段。在Linux平臺上最好的鏈路層負載均衡開源產品是LVS(Linux Virtual Server)。

輪詢(Round Robin,RR)

所有請求被依次分發到每臺應用服務器上,即每臺服務器需要處理的請求數目都相同,適合於所有服務器硬件都相同的場景。

加權輪詢(Weighted Round Robin,WRR)

根據應用服務器硬件性能的情況,在輪詢的基礎上,按照配置的權重將請求分發到每個服務器,高性能的服務器能分配更多請求。

隨機(Random)

請求被隨機分配到各個應用服務

器,在許多場合下,這種方案都很簡單實用,因爲好的隨機數本身就很均衡。即使應用服務器硬件配置不同,也可以使用加權隨機算法。

最少連接(Least Connections)

記錄每個應用服務器正在處理的連接數(請求數),將新到的請求分發到最少連接的服務器上,應該說,這是最符合負載均衡定義的算法。同樣,最少連接算法也可以實現加權最少連接。

源地址散列(Source Hashing)

根據請求來源的IP地址進行Hash計算,得到應用服務器,這樣來自同一個IP地址的請求總在同一個服務器上處理,該請求的上下文信息可以存儲在這臺服務器上,在一個會話週期內重複使用,從而實現會話黏滯。

6.3 分佈式緩存集羣的伸縮性設計

分佈式緩存集羣的伸縮性不能使用簡單的負載均衡手段來實現。

事實上,如果不需要考慮緩存服務器集羣伸縮性,餘數Hash幾乎可以滿足絕大多數的緩存路由需求。

但是,當分佈式緩存集羣需要擴容的時候,事情就變得棘手了。

大部分的業務數據讀操作請求事實上是通過緩存獲取的,只有少量讀操作請求會訪問數據庫,因此數據庫的負載能力是以有緩存爲前提而設計的。當大部分被緩存了的數據因爲服務器擴容而不能正確讀取時,這些數據訪問的壓力就落到了數據庫的身上,這將大大超過數據庫的負載能力,嚴重的可能會導致數據庫宕機

計算機領域有句話:計算機的任何問題都可以通過增加一個虛擬層來解決。計算機硬件、計算機網絡、計算機軟件都莫不如此。計算機網絡的7層協議,每一層都可以看作是下一層的虛擬層;計算機操作系統可以看作是計算機硬件的虛擬層;Java虛擬機可以看作是操作系統的虛擬層;分層的計算機軟件架構事實上也是利用虛擬層的概念。

顯然每個物理節點對應的虛擬節點越多,各個物理節點之間的負載越均衡,新加入物理服務器對原有的物理服務器的影響越保持一致(這就是一致性Hash這個名稱的由來)。

6.4 數據存儲服務器集羣的伸縮性設計

緩存的目的是加速數據讀取的速度並減輕數據存儲服務器的負載壓力,因此部分緩存數據的丟失不影響業務的正常處理,因爲數據還可以從數據庫等存儲服務器上獲取。

而數據存儲服務器必須保證數據的可靠存儲,任何情況下都必須保證數據的可用性和正確性

具體說來,又可分爲關係數據庫集羣的伸縮性設計和NoSQL數據庫的伸縮性設計。

在大型網站的實際應用中,即使進行了分庫和主從複製,對一些單表數據仍然很大的表,比如Facebook的用戶數據庫,淘寶的商品數據庫,還需要進行分片,將一張表拆開分別存儲在多個數據庫中。

Cobar的伸縮有兩種:Cobar服務器集羣的伸縮和MySQL服務器集羣的伸縮。

Cobar服務器可以看作是無狀態的應用服務器,因此其集羣伸縮可以簡單使用負載均衡的手段實現

但由於Cobar路由後只能在單一數據庫實例上處理查詢請求,因此無法執行跨庫的JOIN操作,當然更不能執行跨庫的事務處理。

從業務上回避分佈式關係數據庫的各種缺點:避免事務或利用事務補償機制代替數據庫事務;分解數據訪問邏輯避免JOIN操作等。

NoSQL,主要指非關係的、分佈式的數據庫設計模式。也有許多專家將NoSQL解讀爲Not Only SQL,表示NoSQL只是關係數據庫的補充,而不是替代方案。一般而言,NoSQL數據庫產品都放棄了關係數據庫的兩大重要基礎:以關係代數爲基礎的結構化查詢語言(SQL)和事務一致性保證(ACID)。而強化其他一些大型網站更關注的特性:高可用性和可伸縮性。

6.5 小結

一個具有良好伸縮性架構設計的網站,其設計總是走在業務發展的前面,在業務需要處理更多訪問和服務之前,就已經做好充足準備,當業務需要時,只需要購買或者租用服務器簡單部署實施就可以了,技術團隊亦可高枕無憂

高手定律:這個世界只有遇不到的問題,沒有解決不了的問題,高手之所以成爲高手,是因爲他們遇到了常人很難遇到的問題,並解決了。

救世主定律:遇到問題,分析問題,最後總能解決問題。如果遇到問題就急匆匆地從外面挖一個高手,然後指望高手如探囊取物般輕鬆搞定,最後怕是隻有彼此抱怨和傷害。許多問題只是看起來一樣,具體問題總是要具體對待的,沒有銀彈,沒有救世主。所以這個定律準確地說應該是“沒有救世主定律”。

7 隨需應變:網站的可擴展架構

經常聽到各種場合中對擴展性和伸縮性的誤用,包括許多資深網站架構師也常常混淆兩者,用擴展性表示伸縮性

擴展性(Extensibility)

指對現有系統影響最小的情況下,系統功能可持續擴展或提升的能力。表現在系統基礎設施穩定不需要經常變更,應用之間較少依賴和耦合,對需求變更可以敏捷響應。

伸縮性(Scalability)

指系統能夠通過增加(減少)自身資源規模的方式增強(減少)自己計算處理事務的能力。如果這種增減是成比例的,就被稱作線性伸縮性。在網站架構中,通常指利用集羣的方式增加服務器數量、提高系統的整體事務吞吐能力。

7.1 構建可擴展的網站架構

開發低耦合系統是軟件設計的終極目標之一,度量一個開發框架、設計模式、編程語言優劣的重要尺度就是衡量它是不是讓軟件開發過程和軟件產品更加低耦合。

低耦合的模塊更容易複用,一個低耦合的系統設計也會讓開發過程和維護變得更加輕鬆和容易管理。

完全沒有耦合就是沒有關係,也就無法組合出一個強大的系統。那麼如何分解系統的各個模塊、如何定義各個模塊的接口、如何複用組合不同的模塊構造成一個完整的系統,這是軟件設計中最有挑戰的部分。

筆者認爲,軟件架構師最大的價值不在於掌握多少先進的技術,而在於具有將一個大系統切分成N個低耦合的子模塊的能力,這些子模塊包含橫向的業務模塊,也包含縱向的基礎技術模塊。這種能力一部分源自專業的技術和經驗,還有一部分源自架構師對業務場景的理解、對人性的把握、甚至對世界的認知。

設計網站可擴展架構的核心思想是模塊化,並在此基礎之上,降低模塊間的耦合性,提高模塊的複用性。

在大型網站中,這些模塊通過分佈式部署的方式,獨立的模塊部署在獨立的服務器(集羣)上,從物理上分離模塊之間的耦合關係,進一步降低耦合性提高複用性。

模塊分佈式部署以後具體聚合方式主要有分佈式消息隊列和分佈式服務。

7.2 利用分佈式消息隊列降低系統耦合性

如果模塊之間不存在直接調用,那麼新增模塊或者修改模塊就對其他模塊影響最小,這樣系統的可擴展性無疑更好一些。

事件驅動架構(Event Driven Architecture):通過在低耦合的模塊之間傳輸事件消息,以保持模塊的鬆散耦合,並藉助事件消息的通信完成模塊間合作。

消息發送者將消息發送至分佈式消息隊列即結束對消息的處理,而消息接受者只需要從分佈式消息隊列獲取消息後進行處理,不需要知道該消息從何而來。

隊列是一種先進先出的數據結構,分佈式消息隊列可以看作將這種數據結構部署到獨立的服務器上,應用程序可以通過遠程訪問接口使用分佈式消息隊列,進行消息存取操作,進而實現分佈式的異步調用

在伸縮性方面,由於消息隊列服務器上的數據可以看作是被即時處理的,因此類似於無狀態的服務器,伸縮性設計比較簡單。

在可用性方面,爲了避免消費者進程處理緩慢,分佈式消息隊列服務器內存空間不足造成的問題,如果內存隊列已滿,會將消息寫入磁盤,消息推送模塊在將內存隊列消息處理完以後,將磁盤內容加載到內存隊列繼續處理。

7.3 利用分佈式服務打造可複用的業務平臺

如果說分佈式消息隊列通過消息對象分解系統耦合性,不同子系統處理同一個消息;那麼分佈式服務則通過接口分解系統耦合性,不同子系統通過相同的接口描述進行服務調用。

解決方案就是拆分,將模塊獨立部署,降低系統耦合性。拆分可以分爲縱向拆分和橫向拆分兩種。

縱向拆分:將一個大應用拆分爲多個小應用。

橫向拆分:將複用的業務拆分出來,獨立部署爲分佈式服務,新增業務只需要調用這些分佈式服務,不需要依賴具體的模塊代碼,即可快速搭建一個應用系統,而模塊內業務邏輯變化的時候,只要接口保持一致就不會影響業務程序和其他模塊。

縱向拆分相對較爲簡單,通過梳理業務,將較少相關的業務剝離,使其成爲獨立的Web應用。而對於橫向拆分,不但需要識別可複用的業務,設計服務接口,規範服務依賴關係,還需要一個完善的分佈式服務管理框架。

大型網站需要更簡單更高效的分佈式服務框架構建其SOA(Service Oriented Architecture面向服務的體系架構)。

7.5 利用開放平臺建設網站生態圈

大型網站爲了更好地服務自己的用戶,開發更多的增值服務,會把網站內部的服務封裝成一些調用接口開放出去,供外部的第三方開發者使用,這個提供開放接口的平臺被稱作開放平臺。第三方開發者利用這些開放的接口開發應用程序(APP)或者網站,爲更多的用戶提供價值。網站、用戶、第三方開發者互相依賴,形成一個網站的生態圈,既爲用戶提供更多的價值,也提高了網站和第三方開發者的競爭能力和盈利能力。

API接口:是開放平臺暴露給開

發者使用的一組API,其形式可以是RESTful、WebService、RPC等各種形式。

協議轉換:將各種API輸入轉換成內部服務可以識別的形式,並將內部服務的返回封裝成API的格式。

安全:除了一般應用需要的身份識別、權限控制等安全手段,開放平臺還需要分級的訪問帶寬限制,保證平臺資源被第三方應用公平合理使用,也保護網站內部服務不會被外部應用拖垮。

審計:記錄第三方應用的訪問情況,並進行監控、計費等。

路由:將開放平臺的各種訪問路由映射到具體的內部服務。

流程:將一組離散的服務組織成一個上下文相關的新服務,隱藏服務細節,提供統一接口供開發者調用。

7.6 小結

馬克思的勞動價值理論告訴我們,產品的內在價值在於勞動的時間,勞動的時間不在於個體付出的勞動時間,而在於行業一般勞動時間,資本家只會爲行業一般勞動時間買單,如果你的效率低於行業一般勞動時間,對不起,請你自願加班。反之,如果你有一個更具有擴展性的網站架構,可以更快速地開發新產品,也許你也享受不了只上半天班的福利,但是至少在這個全行業加班的互聯網領域,你能夠按時下班,陪陪家人,看看星星。


更多精彩內容請關注下方微信公衆號:

qrcode_for_gh_7159fb337d37_258.jpg


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章