設計全球級的分布式、任務關鍵型應用 - 從實際項目中得來的教訓(上)
此篇為設計下一代智能DNS和流量管理平臺的 NSONE 公司的創建者和CEO Kris Beevers 的客座文章的 第一部分 。點此閱讀 第二部分
【編者的話】隨著越來越多初創科技公司的出現,如何設計全球級的分布式、任務關鍵性應用成為很多人關注的問題。如何在企業發展之初就考慮到系統的可擴展性?哪些才是在初期應該關注的重點呢?NSONE創始人和CEO Kris Beevers在博客中分享了自己進行大規模應用程序的開發經驗,希望讀者可以從中有所借鑒。
關于Kris Beevers
Kris Beevers 一直以來都在進行全球級的分布式大規模應用程序的設計工作。當年,他曾經逃課一年來為一個文件共享的初創項目來設計后端框架。而且,項目用戶很快增加到了數百萬。然而,RIAA律師的出現,導致了項目流產。Kris也不得不回學校繼續學習。但是,通過這件事,Kris開始在擴展系統方面積累了一定的經驗。
之后,Kris又曾在一家被Internap在2011年收購的互聯網框架提供商——Voxel公司工作。當時,他負責構建供很多大型網絡公司使用的全球互聯網框架。具體來說,他負責構建全球級的分布式公有云、裸機即服務以及內容傳輸網絡等等。從中,Kris遇到了系統擴展中可能碰到的許多問題,收獲了很多經驗和教訓。
現在,作為 NSONE 的創始人和CEO,Kris在NSONE負責設計下一代智能DNS和流量管理平臺。該平臺服務于一些最大的互聯網公司,其中很多是任務關鍵服務的提供商。因此,該平臺是一個真正全球級的分布式、任務關鍵框架。隨著NSONE平臺的擴展,Kris以往的工作經歷為完成在NSONE的工作提供了很多幫助。
接下來,Kris分享了一些自己的經驗,希望讀者可以把這些經驗應用到其他項目應用中,避免走很多彎路。
每一個科技公司都會思考一個問題——隨著公司業務的增長,如何應對應用程序或者系統在擴展時所不可避免遇到的各種挑戰?由此,在設計任務關鍵技術時,一系列的基本問題就包括: 如何從一開始就把可擴展性放入到考慮范圍內,從而使得公司以后可以穩步、健康的發展呢 ?哪些才是值得現在密切關注的關鍵性挑戰呢?而當建立一個分布式的框架時,無論是以可靠性為目標還是性能為目標,這些問題都很難回答。
使用正確的架構和流程可以使得系統和公司能夠經受得住分布式、高流量應用程序所帶來的沖擊。由此,初創人員就可以進行不受約束的應用擴展、有效管理網絡和系統失效、實時解決產品遇到的問題,從而引導公司和產品穩步成長。
從架構開始
當編寫代碼時,如果你總是試圖使其盡可能的高效,同時又盡可能的少占用計算和存儲資源。那么,停止這么干吧。
在現代分布式系統中,限制系統可擴展性的基本上不是系統的垂直深度或者特定角色內代碼的效率,而是不同交互系統之間通信的效率和架構中每個角色的水平可擴展性。因此, 不要優化你的代碼,優化你的架構 !
微服務是一個好想法。它們可以把應用中相互獨立的角色進行解耦合,從而可以單獨擴展其中的每一個。 在擔心深度之前,請先計劃如何水平擴展角色。 服務器速度快、便宜,而且性價比還在不斷提高。然而,開發者的工資卻在不斷提高。因此,盡可能的使用水平擴展。只有當代碼真的有問題時,再考慮代碼優化。
因此,在寫代碼之前,請先花時間好好思考一下架構——畫一畫框圖、考慮一下通信和數據約束、形成一份好的計劃書。
以角色為單位來考慮系統
一個微服務架構在一定程度上表明你正在設計一個由解耦合的子系統所組成的應用。這些子系統中的每一個都解決一個特定的問題或者擔當著特殊的角色。但是,需要重申的是,你應該以角色為單位來考慮你的架構—— 不僅僅是因為它解耦合了擴展約束,也因為它影響你設計代碼、部署系統和建設框架的方式。
一個由解耦合、互相通信的角色所組成的應用可以以進程、VM或者容器的方式在一個單獨的服務器中運行。這就使得你可以把應用部署在本地的開發環境、小的系統或者大的產品框架中。
隨著應用的產品框架發展和流量及用戶的增加,你可以把那些需要單獨的系統或者集群的角色分離出去。但是,在你的MVP中,你可以通過組合產品角色來為擴展做好準備。NSONE的最早alpha版本就部署在AWS中一個小的單獨的單播t2實例中。現在,NSONE的產品框架已經擴展到幾百個服務器,并在全球擁有30個產品設施。隨著系統的增長,Kris把關鍵性的子系統分離到單獨的服務器或者集群中,并在需要時添加寬度和冗余。
跨越全球級的分布式系統進行通信是十分困難的
去建立并擴展一個初始只部署在一個服務器、后期部署在一個數據中心的應用是一回事。解決多個數據中心間應用程序的數據傳輸的難題又是另外一回事。當你的子系統需要與本地之外的系統進行通信時,通信的可靠性就變得十分脆弱。這時,你需要應對的就是服務器失效和通信失效這樣的雙重難題。
通過互聯網進行連接是十分脆弱和不可預知的。在NSONE,Kris等設計系統時就假設邊界DNS傳輸設備會經常性的與核心設備失去連接。在非洲、巴西和印度等市場,這是經常發生的事情。但是,需要保證的是,當通信恢復時,重新聚合一定要快速。慎重考慮命令和控制消息的創建以及排隊策略是其中的核心。
考慮不同設備間不同通信模式的通信也十分重要。 如果你正在發送延遲敏感的關鍵命令和控制消息到一個或多個設備,你可能需要考慮魯棒的消息排隊系統 ;如果發送的是非關鍵信息,你可能考慮 更加輕量和可靠性略差的系統 ;如果你發送的是需要嚴格同步的應用數據, 帶有魯棒WAN響應的現代數據庫可能是不錯的選擇 。當在不同設備間通信時,你需要根據任務選擇正確的工具。
對于同步和分片而言,一致性哈希是一個殺手锏武器
現在,是時候來考慮水平擴展了。你已經擁有了一些相互之間通過局域網或者互聯網進行通信的子系統。為了高效服務前端的請求,系統需要盡可能的把請求發送到本地的后端進行處理。但是,在一個特殊角色中的服務器不可能是萬能的。那么,究竟選擇一個水平層中的哪個節點來處理跨地域的請求或者任務呢?
直觀而言,你可以配置一個靜態或動態的路由表——擁有某些特定ID的請求發送到特定的服務器。但是,當系統擴展時,路由表的擴展將會十分痛苦。例如,你可以把請求id哈希到一個服務器。一旦一個新的服務器添加進來,所有的請求就都需要被重新路由,數據或者緩沖的局部性一下就會被打破,由此可能引起系統崩潰。當然,你也可以設計一個十分復雜的協議來解決這一問題。但是,通常情況下,這些協議都效率低下,且容易出問題。
一致性哈希 應對這種情況十分有效。它可以在不進行實際通信的情況下,保證所有節點遵循同樣的協議。而且,它易于實現和擴展,子系統資源變化時所引起的代價也很小。在一個擁有很多交互角色的復雜系統中,使用一致性哈希來分配多個節點集合間消息和請求是一個十分明智的選擇。
在NSONE, Kris等采用了各種方式來使用一致性哈希,包括把DNS請求路由到特定的CPU核來最大化緩存一致性、不進行通信的情況下協商監控節點的任務以及聚合節點跨層大容量數據的分片等等 。
測量和監控一切東西
這是一個老生常談,但又不得不再次強調的東西。 不測量,你就不可能充分理解 。即使當你沒有遇到擴展性挑戰的時候,充分理解系統行為也能在將來幫助你了解系統擴展時的瓶頸所在。
當然,不要只是收集一些系統度量。 要讓你的應用能夠理解數據庫響應時間、消息延遲、緩存命中率、內存碎片以及磁盤I/O等。 首先,收集這些信息,并理解系統正常運轉時的值。這樣,系統發生異常時,只要重新檢查這些值中有哪些異常,就可以很快定位問題所在。而且,當一個新類型的負載或者額外的流量添加到系統中時,你可以迅速感知,并相應的調整架構或子系統。
在NSONE, Kris等就認真研究了度量值 ,并將其時刻顯示在監視屏幕上。當發現異常時,團隊人員會問為什么,并深入挖掘。通過這種深入理解,NSONE團隊可以非常清楚平臺對新的流量和負載的反應,從而特別自信的服務互聯網中最大客戶的DNS和流量管理需求。
與測量同樣重要的是監控。測量和監控是兩個不同的東西——測量是為了更好的理解系統行為,而監控則是探測行為中存在的異常。在項目成長過程中,監控和警告可以幫助你理解哪些異常是無害的、哪些是有害的。在項目啟動之初,你可能花費很多時間來區分這些異常。但是當項目發展壯大以后,你就可以根據之前的理解,只關注那些可能影響服務的異常,避免重新設計系統的架構。
從多個有利的點來進行監控。不僅僅要從多個物理位置,還要從多個邏輯位置來進行監控。在NSONE,Kris等就監控內部異常監測數據,使用Catchpoint和Raintank等第三方服務進行由外而內的監控,而且利用多個數據源來觀察平臺和網絡事件。
同樣重要的是, 監控不僅僅是只看到系統的狀態 。對于一個系統而言,提供好的服務是什么意思?是低延遲的響應時間?還是高的緩存命中率?監視這些度量,當他們超出正常范圍時,發出警告。
點此繼續閱讀文章的 第二部分