我們生活在一個時代,大規模的,面向互聯網的系統(例如Google,Amazon,Facebook等)是工程界的偶像。 他們每秒處理大量請求,并管理規模空前的數據存儲庫。 出于商業秘密的原因,在這些系統上獲取準確的流量數字并不容易。 但是,這些站點隨著使用量的增長而迅速擴展的能力是其持續成功的關鍵因素。
幸運的是,我們可以通過一家技術公司的年度使用情況報告深入了解以Internet規模處理的請求和數據量。 從2019年起,您可以在此瀏覽其令人難以置信的詳細使用情況統計信息。這是對大規模系統功能的令人著迷的一瞥。 不過請注意,這是Pornhub.com。 該報告不適合該報告。 以下是PG-13的一個示例性數據點-他們在2019年有420億的訪問量!
對于絕大多數企業和政府系統而言,可擴展性并不是開發和部署初期的主要質量要求。 增強可用性和實用性的新功能是我們開發周期的驅動力。 只要正常負載下的性能足夠,我們就會繼續添加面向用戶的功能,以提高系統的業務價值。
盡管如此,系統發展成為一種狀態,在這種狀態下,增強的性能和可伸縮性變得迫在眉睫,甚至是生存,這并不少見。 引人注目的功能和高實用性催生了成功,這帶來了更多的處理請求和更多的數據管理。 這通常預示著一個臨界點,在輕載下有意義的設計決策現在突然變成了技術負擔。 外部觸發事件通常會導致這些轉折點-在2020年3月的媒體上,許多報道稱政府失業和超市在線訂購網站由于冠狀病毒大流行而在需求下崩潰。
當達到這個臨界點時,架構師有責任帶領系統演進為高響應,可擴展的系統。 核心系統的架構機制和模式將需要重新設計,以使其能夠應對不斷增長的需求。 對于許多架構師而言,這是未知的或陌生的領域,因為可伸縮性使我們走下了有時對于廣泛理解的軟件體系結構原理而言異端的道路。
以下六個經驗法則代表著每位軟件架構師必須幫助他們構建可伸縮系統的知識。 這些通用規則可以為架構師在可靠地處理不斷增長的請求負載和數據量的過程中提供指導。
成本和可擴展性有著不可磨滅的聯系
擴展系統的核心原則是能夠輕松添加新的處理資源來處理增加的負載。 對于許多系統,一種簡單有效的方法是部署多個無狀態服務器資源實例,并使用負載平衡器在這些實例之間分配請求(請參見圖1)。 假設將這些資源部署在Amazon Web Services之類的云平臺上,則基本費用為:
· 每個服務器實例的虛擬機部署成本
· 負載均衡器的成本,取決于新請求和活動請求的數量以及處理的數據量
在這種情況下,隨著請求負載的增長,就需要運行服務器代碼的已部署虛擬機具有更大的處理能力。 這導致更高的成本。 您的負載均衡器成本還將與您的請求負載和數據大小成比例地增長。
> Figure 1 A Simple Load Balancing Example
因此,成本和規模是并存的。 您關于可伸縮性的設計決策不可避免地會影響您的部署成本。 忘記了這一點,您可能會發現自己以及許多知名公司在一個月底收到了意外大筆的部署費用!
在這種情況下,您的設計如何降低成本? 主要有兩種方法:
· 使用彈性負載平衡器可根據瞬時請求負載來調整服務器實例的數量。 然后,在交通繁忙期間,您需要為最少數量的服務器實例付費。 隨著請求量的增長,負載均衡器會產生新的實例,并且容量也會相應地增長。
· 增加每個服務器實例的容量。 這通常是通過調整服務器部署參數(例如線程數,連接數,堆大小等)來完成的。 默認平臺設置很少適合您的工作負載。 仔細選擇參數設置可以顯著提高性能,從而提高容量。 您基本上是在使用相同的資源來完成更多工作-這是實現擴展的關鍵原則。
您的系統存在瓶頸。 某處!
擴展系統實質上意味著增加其容量。 在上面的示例中,我們通過在負載平衡的計算資源上部署更多服務器實例來提高請求處理能力。 但是,軟件系統包括多個從屬處理元素或微服務。 不可避免地,隨著某些微服務中容量的增加,您將淹沒其他微服務中的容量。
在我們的負載均衡示例中,假設我們的服務器實例均具有到同一共享數據庫的連接。 隨著部署服務器數量的增加,我們增加了數據庫上的請求負載(請參見圖2)。 在某個階段,此數據庫將達到飽和,并且數據庫訪問將開始引起更多的延遲。 現在,您的數據庫已成為瓶頸,無論您增加更多的服務器處理能力都無所謂。 為了進一步擴展,解決方案是以某種方式增加數據庫容量。 您可以嘗試優化查詢,或添加更多的CPU和/或內存。 也許復制和/或分片數據庫。 有很多可能性。
> Figure 2 Increasing Server Capacity creates a Bottleneck at the Database
系統中的任何共享資源都可能成為限制容量的瓶頸。 當您增加部分架構的容量時,您需要仔細查看下游的容量,以確保不會因請求突然而意外地淹沒您的系統。 這會迅速導致級聯故障(請參閱下一條規則),并使整個系統崩潰。
數據庫,消息隊列,長時間等待的網絡連接,線程和連接池以及共享的微服務都是瓶頸的主要候選者。 您可以放心,高流量負載將迅速暴露這些元素。 關鍵是防止瓶頸暴露時突然崩潰,并能夠快速部署更多容量。
慢服務比失敗服務更邪惡
在正常操作下,系統應設計為在微服務和組成系統的數據庫之間提供穩定,低延遲的通信。 盡管系統負載保持在您設計的運行配置文件之內,但性能仍然是可預測的,一致且快速的,如圖3所示。
> Figure 3 Low latencies under normal load
隨著客戶端負載增加到超出操作配置文件的范圍,微服務之間的請求等待時間將開始增加。 首先,這通常是一個緩慢而穩定的增長,可能不會嚴重影響整個系統的運行,尤其是在負載激增短暫的情況下。 但是,如果傳入請求負載繼續超過容量(服務B),則未完成的請求將開始備份在發出請求的微服務(服務A)中,由于下游等待時間較慢,該服務現在看到的傳入請求多于已完成的請求。 如圖4所示。
> Figure 4 Increased load causes longer latencies and requests to back up
在這種情況下,事情可能真的會迅速消退。 當一項服務不堪重負,并且由于顛簸或資源耗盡而基本停止運行時,請求的服務將無法響應其客戶,而客戶也將停止運行。 導致的結果稱為級聯故障-緩慢的微服務導致請求沿著請求處理路徑建立,直到整個系統突然出現故障。
這就是為什么緩慢的服務是邪惡的,而不是不可用的服務更是如此。 如果您呼叫失敗的服務或由于瞬態網絡問題而進行了分區的服務,則您會立即收到異常消息,并可以明智地決定做什么(例如退避并重試,報告錯誤)。 逐漸不堪重負的服務正常運行,只是延遲時間更長。 這暴露了所有依賴服務中的潛在瓶頸,最終,出了一些大問題。
斷路器和隔板等架構模式可防止級聯故障。 如果對服務的延遲超過指定值,則斷路器可以限制請求負載或釋放請求負載。 如果只有一個下游服務依賴項失敗,則隔板可以保護整個微服務免于失敗。 使用它們來構建彈性以及高度可擴展的體系結構。
數據層最難擴展
核心業務數據示例是客戶資料,交易和帳戶余額。 這些必須正確,一致且可用。
操作數據示例包括用戶會話時長,每小時訪問者和頁面瀏覽量。 該數據通常具有保存期限,并且可以隨時間進行匯總和匯總。 如果還不是100%完成,那就不是世界末日。 因此,我們可以更輕松地捕獲和存儲帶外操作數據,例如將其寫入日志文件或消息隊列。 消費者然后定期檢索數據并將其寫入數據存儲。
隨著系統請求處理層的擴展,共享事務數據庫將承受更多負載。 隨著查詢負載的增加,這些可能迅速成為瓶頸。 查詢優化是一個有用的第一步,添加更多內存以使數據庫引擎能夠緩存索引和表數據也是如此。 最終,您的數據庫引擎將耗盡所有精力,并且需要進行更根本的更改。
首先要注意的是,任何數據組織的更改都可能在數據層帶來痛苦。 如果更改關系數據庫中的架構,則可能必須運行腳本以重新加載數據以匹配新架構。 在腳本運行期間(對于大型數據庫而言可能是很長的時間),系統無法進行寫入。 這可能不會使您的客戶滿意。
NoSQL,無模式數據庫減輕了重新加載數據庫的需要,但是您仍然必須更改查詢級代碼才能識別已修改的數據組織。 如果您擁有業務數據集合,其中某些項目的格式已修改,而某些項目的原始格式已修改,則可能還需要管理數據對象的版本控制。
進一步擴展可能需要分發數據庫。 帶有只讀副本的領導者追隨者模型可能就足夠了。 對于大多數數據庫來說,這很容易設置,但是需要密切的持續監視。 領導者死亡時,故障轉移很少是瞬時的,有時需要手動干預。 這些問題都是非常依賴數據庫引擎的。
如果采用無領導者方法,則必須決定如何最好地在多個節點之間分配和分區數據。 在大多數數據庫中,一旦選擇了分區鍵,就無法在不重建數據庫的情況下進行更改。 不用說,分區鍵需要明智地選擇! 分區鍵還控制如何在節點之間分配數據。 隨著添加節點以提供更大的容量,需要進行重新平衡以在新節點上分布數據和請求。 同樣,數據庫文檔的內容中描述了它的工作方式。 有時它并不像它可能的那樣平滑。
由于分布式數據庫的潛在管理難題,基于托管的基于云的替代方案(例如AWS Dynamodb,Google Firestore)通常是首選。 當然,需要權衡取舍-這是另一個故事的主題!
這里的信息很簡單。 更改邏輯和物理數據模型以擴展查詢處理能力很少是一個平穩而簡單的過程。 想要不經常面對的人。
快取! 快取! 并緩存更多!
減輕數據庫負載的一種方法是避免盡可能訪問它。 這就是緩存的用處。您友好的數據庫引擎應該能夠充分利用節點緩存上的資源。 這是一個簡單且有用的解決方案,如果可能會很昂貴。
更好的是,如果不需要,為什么還要查詢數據庫? 對于經常讀取且很少更改的數據,可以修改您的處理邏輯以首先檢查分布式緩存,例如memcached服務器。 這需要遠程調用,但是如果您需要的數據在高速緩存中,則在快速網絡上,這比查詢數據庫實例要便宜得多。
引入緩存層需要修改您的處理邏輯,以檢查緩存數據。 如果所需的內容不在高速緩存中,則您的代碼仍必須查詢數據庫,然后將結果加載到高速緩存中并將其返回給調用方。 您還需要決定何時刪除或使緩存的結果無效-這取決于您的應用程序向客戶提供陳舊結果的容忍度。
設計良好的緩存方案對于擴展系統絕對是無價的。 如果您可以處理來自緩存的大部分讀取請求,則可以在數據庫上購買額外的容量,因為它們不參與處理大多數請求。 這意味著您可以避免復雜且痛苦的數據層修改(請參閱上一條規則),同時為越來越多的請求創建容量。 這是使大家開心的秘訣! 甚至會計師。
監控是可伸縮系統的基礎
這是很多工作。 而且很難使所有事物都代表現實,因此您可以獲得有意義的結果。 毫不奇怪,它很少完成。
替代方法是監視。 對系統的簡單監視涉及確保基礎結構正常運行。 如果資源不足,例如內存或磁盤空間不足,或者遠程調用失敗,則應向您發出警報,以便在真正的不良情況發生之前可以采取補救措施。
以上監視見解是必要的,但不足。 隨著系統的擴展,您需要了解應用程序行為之間的關系。 一個簡單的示例是,隨著并發寫入請求數量的增加,數據庫的寫入性能如何。 您還想知道何時由于下游延遲增加而使斷路器在微服務中跳閘,負載平衡器何時開始產生新實例或消息在隊列中保留的時間超過指定的閾值。
有許多可用于監視的解決方案。 Splunk是一個全面而強大的日志聚合框架。 任何云都有其自己的監視框架,例如AWS Cloudwatch。 這些功能使您可以捕獲有關系統行為的指標,并將其顯示在統一的儀表板中,以支持對性能的監視和分析。 術語"可觀察性"通常用于涵蓋監視和性能分析的整個過程。
有兩件事(至少!)要考慮。
首先,要獲得對性能的深入了解,您將需要生成與應用程序行為細節相關的自定義指標。 仔細設計這些指標,并在您的微服務中添加代碼以將其注入到您的監視框架中,以便可以在系統儀表板中對其進行觀察和分析。
其次,監視是系統中的必要功能(和成本)。 它已打開。 總是。 當您需要調整性能和擴展系統時,捕獲的數據將指導您的實驗和工作。 在系統演進中以數據為驅動力有助于確保您花費時間修改和改進系統部分,這些部分對于支持性能和擴展要求至關重要。
結論
高性能和可伸縮性通常不是我們構建的許多系統的優先質量要求。 通常,了解,實施和發展功能需求是很成問題的,以致浪費所有可用的時間和預算。 但是有時,在外部事件或意外成功的驅動下,可伸縮性是必要的,否則您的系統將因負載不足而崩潰。 不可用的系統(或由于性能降低而實際上不可用)對任何人都沒有用。
-
互聯網
+關注
關注
54文章
11181瀏覽量
103597 -
軟件架構
+關注
關注
0文章
64瀏覽量
10297
發布評論請先 登錄
相關推薦
評論