知名網站的技術實現
文 / 林昊
網站需要具有良好的可伸縮性,來應對不斷增長的訪問量和數據量。本文結合 Google、非死book 等知名網站的技術發展歷程,闡述了它們在可伸縮性上的技術實現。
《程序員》雜志 5 月刊的《可伸縮性的 10 年探索:知名網站的技術發展歷程》一文中介紹了一些 Alexa 排名較前的網站的技術發展歷程,本文將結合提及的 Google、非死book、推ter 等網站的技術發展歷程,總結它們在可伸縮性、可用性、高性能以及低成本四點上通常采用的技術。
可伸縮
可伸縮分為垂直伸縮和水平伸縮兩類,垂直伸縮通過升級機器的硬件來解決問題,水平伸縮通過增加機器來解決問題。不同網站在可伸縮上采用了不同的策略。例如,Google 完全依賴水平伸縮來解決問題,而其他網站多數是依賴垂直伸縮來解決數據存儲問題。
垂直伸縮要求軟件要能在硬件升級時,發揮出硬件的能力,例如可配置的并行數、內存等。硬件的發展速度非常迅猛,網站的機器配置自然也是每年都在升級,因此軟件時刻都在被檢驗是否能垂直伸縮。
水平伸縮主要解決的是如何僅通過增加機器就能解決問題,一般通過應用層和存儲層兩個層次來解決。
在應用層做到水平伸縮,通常采用的策略是 Share Nothing/Stateless,將狀態信息放入客戶端或存儲層(例如用戶的會話信息放入 Cookie,或放入服務器端的緩存系統)。此時,在訪問量上漲后增加機器即可。在應用系統由單臺增加到多臺組成集群時,需要引入負載均衡,可能是硬件的 也可能是軟件的。
我們發現,就應用層的結構演變而言,前面提到的 Google、非死book、推ter 等網站在應用層上最后形成的結構幾乎完全一樣,均為前端 Web 系統集群 + Services 集群。通常到了一定階段后,前端 Web 系統和 Services 又會按照一定的規則來進行拆分,如按業務領域等。可見,其實 SOA 在大網站中已經落地,而不像企業領域中宣傳的那么虛。
存儲層實現水平伸縮,難度就比應用層大多了。從 Google、非死book、推ter 等幾家網站的發展歷程也可看出,解決存儲層問題需要花費大量的時間和精力,而且由于業務發展的壓力較大,很多網站開始會采用垂直伸縮方式來解決存儲層的問題。
實現存儲層的可伸縮性,通常采用的方案是單點寫(指在某個粒度的單點,對 HBase 而言,單行數據一定在同一臺機器上進行寫操作),但讀可能是多點。讀多點,主要需要考慮不一致的問題。無論讀是單點還是多點,數據都會在軟件層面做到寫多 份,策略主要有同步寫多份、投票寫多份、異步復制最終一致等(例如 HDFS、Cassandra、ZooKeeper),采用自動分裂方法來實現數據量增長時的自動伸縮,采用一致性 Hash 或根據某種規則的自動均衡策略等來實現機器增減時的相應處理,同時也需要有感知機器增減的方法(例如采用 ZooKeeper)。
可以看出,要在存儲層上實現可伸縮,技術上的難點很多。這也是為什么大規模網站都不在數據庫上做復雜的運算,而只是把其當做一種存儲信息的方式來使用。存儲過程等基本不會出現,以降低數據庫的壓力。
除了應用層和存儲層需要做到可伸縮外,在設計系統時硬件層面的可伸縮也是需要考慮的。例如,硬件負載設備只能支撐到一定流量,同樣也要考慮如何讓其能夠可伸縮。
除了盡可能做到系統可伸縮外,減少對系統的壓力也是緩解系統的可伸縮所面臨挑戰的方法,例如批量提交、最終一致、油Tube 提到的“欺騙”、適當的正確性等策略。
可伸縮性是網站從小到大要經歷的第一關挑戰,同時隨著網站的不斷發展,還要不斷地做技術改造才能保證網站在每個階段(如同機房階段、同城多機房階段、異地多機房階段)都具備優秀的可伸縮性。不過,幸運的是不同階段的可伸縮性方案都已經比較成熟了。
可用性
可伸縮性能保證網站在訪問量和數據量不斷增長的情況下生存下來,同時也從某種程度上保證了網站的可用性。但除此之外,要保障系統的可用性,還需付出很多努力。
設計高可用性系統時,避免單點是其中最重要的一點,一般采用主備或集群等方法來避免單點。例如,負載均衡設備、MySQL 等通常采用主備方式。在 BigTable 里采取了一種比較特殊的方法來避免 Tablet Server 的單點,即通過 Chubby 來感知 Tablet Server 的健康狀況,如發現 Tablet Server 掛掉了,則由 Master 將此 Tablet Server 負責的 Tablet 遷移到其他的 Tablet Server 上。
除了避免單點外,降低耦合也是設計高可用性系統時應考慮的重點。通常采取異步化來降低耦合,將非關鍵邏輯和關鍵邏輯隔離開。例如,打開天貓的商 品詳情時,都會有相關商品的推薦。如果這個推薦系統出問題的話,就會導致商品詳情也看不到了,因此這里可以異步載推薦系統,通常采用 AJAX 帶超時的方式來實現。
Google、非死book 等網站在總結多年的可用性系統設計經驗時,列入了同一條設計原則,即保持簡單。簡單的方案一般比較容易掌控,而復雜的方案一方面實現難度大,另一方面出現問題時很難排查。
可控性也是各網站強調的重點,可控性意味著一切代碼都在掌控之下,并且最好是每個使用到的部分都由專業人員負責(對可用性要求越高,在這點上要 求也就越高)。這樣在出現問題時才能清楚是哪個地方造成的,并且可以自行排查和解決。如不可控,則意味著一旦出現問題,就得依賴第三方來排查,時間上非常 不可控。這也是網站不愿意采用商用軟件的重要原因。
編寫高質量軟件是保障系統高可用性的重要一環,可控性是其基礎。Google、非死book 等網站在總結保障高可用的經驗中均提到了監控/報警、容錯、自我保護等策略。
監控/報警是軟件自身能夠保障高可用的重要策略,就像是汽車的儀表盤一樣,可以告訴你油還剩多少、速度是多少、胎壓是否正常等重要信息。對于軟 件而言,同樣需要讓外部獲取到其運行的狀況。例如,Google 的軟件都會提供一個 HTML 頁面供使用者或開發人員訪問(用過 Hadoop 的人也會發現這個特征)。在這個頁面上可通過 key/value 的方式來獲取系統的一些運行指標。對于 RPC 系統而言,Google 會采集所有的正常請求、錯誤請求以及其消耗時間的分布狀況(>0.05s、>0.1s 等)。除了監控系統的運行狀況外,也提供了其他一些方式以便外部能簡單判斷系統運行是否正常,例如 cURL 某頁面等。對于不正常的現象,要及時報警,盡可能做到在故障尚未影響到用戶時解決掉。
軟件的正常運行需要依賴很多外部因素,例如機房、硬件、數據庫、服務等,而所有依賴的部分都有可能出現故障(要堅信這點,互聯網的特色是所有小 概率事件都會發生)。在設計軟件時需要考慮當依賴方出問題時,如何保障軟件的可用性,因此一定要做一些容錯處理。為了避免機房故障,各網站通常會租用或建 設多機房。例如,Google 采用 IDE 硬盤來存儲文件,不做 RAID,于是采用復制三份的策略來避免硬盤故障導致數據丟失。
軟件一般會提供多種功能,有的重要、有的不重要的。而由不重要的功能異常導致重要的功能出現問題,顯然不合算,因此在設計軟件時需要充分考慮異常隔離,不互相影響。例如在 Google 的系統設計中會采用 Prioritized Request 等策略。
James Hamilton 的那篇著名的論文《On Designing and Deploying Internet-Scale Services》中提到的 Graceful Degradation,即為優雅降級。通常采用的方法是在故障將要出現或出現后,關閉系統的某些功能來降低故障產生的影響。例如,網站上有些操作可能特 別耗資源,而這些資源的消耗又可能影響到核心功能,因此一旦出現影響,就可以關閉這些功能保障核心功能可用。降級可以幫助系統臨時繞開故障,而產生故障的 原因需要在事后排查。
交付具有高可用特征的軟件是開發人員的重要職責。而對一個網站而言,軟件不是一次性交付的,也不是好幾年才升級一次,需要頻繁交付,因此維護軟件是保障高可用的重要環節。
多數情況下,系統的不可用是由變更造成的,因此如何降低變更對系統可用性造成的影響成為了各網站都關注的重點。Google 在發布新產品時通常采取“滾木移石”方法,而 非死book 則通常采用 Dark Launch 方法,降低變更帶來的影響。
人工處理系統變更是故障產生的隱患,因此各網站基本都會推薦多種工具來實現系統變更的自動化,例如采用 Puppet 來實現自動化部署。
除了發布這個重要環節外,處理故障也是維護方面的重要工作。系統總有出現故障時,如何快速處理故障以降低對故障可用性的影響也成為網站一直關注 的重點。前面已經講述了可控性對解決故障的幫助。除可控性外,各網站也在研究其他方法。非死book 采用 FBAR 來自動處理部分故障,這顯然可從某種程度上降低故障產生的影響。
性能
前面提到的可控性同樣也是保證性能的重點,只有明確知道調用的每個 API 及所依賴環境(包括軟硬件)的細節原理,才能編寫出高性能軟件。
從系統結構上來看,為了保障高性能,各大網站都采用了類似的方法。首先是前端 Web 系統這塊,都采用了可編譯為機器碼的方式,即便 非死book 采用的是 PHP,其仍然研發了一個可自動轉化為 C++ 代碼的產品來提升運行效率。
設計系統時,應考慮將沒有前后依賴的邏輯并行化處理,或者將大的請求進行拆分。例如,Google 會先將所搜索的內容進行分詞,然后并行進行索引查詢,從而提高響應速度。
基本上各大網站都極度依賴緩存,原因在于,內存的訪問速度遠快于磁盤。在依賴緩存的場景中,最需要做到的是數據一致性的合理保障。一個典型的場 景是數據更新時保障緩存一致性的策略。要將緩存的更新和數據的更新做成一個事務顯然有不小的難度,此時網站常采用一個簡單的策略來保障,就是先失效緩存, 再更新數據,等到下次系統去訪問此數據時,才更新到內存。
除了緩存外,可以看到各大網站都采用了 CDN,CDN 可以讓靜態文件更靠近用戶,便于用戶快速獲取。而除了讓靜態文件更靠近用戶外,多 IDC 的建設除了提升可用性外,還可以讓動態數據更靠近用戶。當然,這在技術上的實現難度會比 CDN 高很多。
除了結構上的這幾點外,技術創新是提升系統性能的重要方法。例如,Google 提高了 TCP 的初始擁塞窗口值等。而要做到技術創新,顯然可控性是基礎。
成本
隨著網站規模的不斷擴大,系統的運行和維護成本將會成為公司中支出的重要部分。例如,有數據表明,騰訊每年支付給運營商的費用在總支出中占比排 行第二(2010年為 208 億)。網站規模越大,成本控制就越重要(潛臺詞是在網站規模不是很大時,也許支撐業務快速發展更重要)。例如,性價比是 Google 設計系統時重要考量的指標。有些網站會采用x元/每千次 PV 來計算成本。
有些性能優化需要增加成本(如緩存和 CDN),而有些性能優化是可以降低成本的(如 Google 對索引結構的優化),因此性能優化通常也是降低成本的一種方法。網站規模大了以后,規模效應可以讓有些性能優化帶來的成本降低非常明顯。
硬件不斷升級,而軟件系統層面上又更多的是靠集群來支撐,因此通常很難完全消耗硬件資源,虛擬化就成為一種不錯的降低成本的方法。虛擬化具備很好的隔離機制,避免了應用間的互相影響,因此落地的難度不大。
除了依靠虛擬化來降低成本之外,Google 采用了自行實現的一種 Shared Environment 方法來降低成本。Shared Environment 可根據不同類型的資源消耗,動態組合(例如分時)部署到同一臺機器上,充分利用資源。
如前所述,網站主要是靠可伸縮性存活下來的,因此隨著規模的擴大,必然會有大量的機器。比如,Google 有上百萬臺機器,非死book 有幾十萬臺機器。在這么大規模下,自行根據應用特征設計機器,會帶來很大的成本下降,因此 Google、非死book 都自行設計機器和數據中心。從 PUE 上可以看出,Google、非死book 自行設計數據中心帶來的成本降低非常可觀。
各網站的情況不同,應對以上四點挑戰所采用的方法不同,每個階段都有自己適用的解決方案。例如,Google 成立初期的主要業務是搜索,主要競爭的是技術,功能次之,而 非死book、eBay 等網站的競爭壓力主要在業務功能上,因此在成立之初必然會有不同的側重點。不用想著一開始就把網站做成 Google、非死book 等現在的結構,適合自己的就是好的。
很多開發人員在加入規模較大的網站后,會覺得系統結構已經穩定了,沒有發展的空間,但從上面各網站的發展歷程來看,可以看出網站對技術的要求是 在不斷演變的。通過觀察大網站的發展歷程,并結合公司的業務背景、知識結構等來判斷其下一步的發展,對個人成長是有很大幫助的。同時可以借此儲備一些技 術,以把握技術演變時的機會,獲得更大的成長。如果開發人員加入了規模尚小的網站,且自身技術儲備不錯的話,就有機會親身經歷網站從小到大的演變了。但這 要看個人如何把握。
圖 1 發展到一定規模后的網站結構
圍繞可伸縮性、可用性、性能和成本這四個方向,在網站發展到一定規模后,通常會演變成如圖 1 所示的結構。
除了在可伸縮性、可用性、性能和成本這四方面的技術挑戰外,網站還面臨其他很多方面的挑戰,例如海量數據分析和挖掘、網站安全、業務發展的靈活 性支撐、人員增長后龐大的軟件管理等,因此構建一個支撐大訪問量、長期發展、低成本運行的網站是需要有堅實的技術背景作為支撐的。
作者林昊,目前就職于淘寶,2007-2010年負責設計和實現淘寶的服務框架,此服務框架在淘寶大面積使用,每天承擔了 150 億+的請求;2011年開始負責 HBase 在淘寶的落地,目前淘寶已有 20 個以上的在線項目在使用 HBase。