在雲端擴展應用程序的 21 種方法

收藏待读

在雲端擴展應用程序的 21 種方法

介紹

在構建 web 應用程序時,可擴展性是非常重要的部分。有多種方法可用於擴展 web 應用程序層和數據庫層。我將通過微軟 Azure 服務示例來解釋這些方法。

1. 擴展 web 應用程序

假設你有一個託管在 VM 上的 web 應用程序。一開始,網站每秒接收 10 個請求。但是現在,在你推出了一個很酷的新產品或服務後,網站每秒會接收到數千個請求。VM 將接收所有的負載,在某個時刻,它將拒絕請求,如果沒有發生宕機,至少會慢下來,這對業務的增長來說是個壞消息。怎樣解決這個問題?你也許會說:我需要更強大的 VM!而你所說的其實是所謂的垂直擴展。

a. 垂直擴展

如果 8GB RAM、13 核處理器和 HDD 硬盤不夠用的話,你可以再啟動另一個 VM。新的 VM 配備了 512GB RAM、Xeon 處理器和最新的 SSD 硬盤。這就是垂直擴展。這是擴展 web 應用程序最容易,也是最快的方法。只需要把 web 應用程序的內容遷移到更大的新 VM 上,無需修改源代碼。Azure 提供高達 448GB 的 VM 專用 RAM。

在雲端擴展應用程序的 21 種方法

擴展到更大的 VM

在遷移到另一個 VM 上時,該應用程序也許會停止運行一段時間,並可能丟失用戶的 HTTP 會話和 Cookie。

即使用這樣巨型的 VM,應用程序還是無法處理所有的負載,那麼垂直擴展就達到其極限了,畢竟 VM 無法擁有無限的 RAM 和 CPU。那麼,如果我們可以在多個 VM 實例上分享這些負載呢?這就是水平擴展。

b. 水平擴展

垂直擴展關注的是使用更強大的單台機器,而水平擴展則是使用多台機器。這樣,我們就可以擁有更多 RAM 和 CPU,不是只在一台 VM 上,而是在 VM 集群上。同樣的解決方案可用於通過更多的處理器來獲得更強大的計算能力,也即從單個處理器 / 線程遷移到多個處理器 / 線程上。

水平擴展需要重新思考應用程序的架構,在某些場景中還需要修改源代碼。

在雲端擴展應用程序的 21 種方法

水平擴展到多實例

這種方法需要一個方式來決定將用戶或 HTTP 請求發送到哪個 VM 實例。這就是負載均衡器或流量管理器。

c. 負載均衡器、流量管理器

負載均衡器和流量管理器用於在多個實例之間分配網絡流量負載。

它們使用了多種算法,以下是其中一些:

輪詢調度(Round Robin):按順序輪流。

最少連接(Least Connection):請求被發送給服務活動會話數量最少的服務器。

鏈式故障轉移:只有在前一個服務器無法接收更多請求時,才將請求重定向到下一個服務器。

加權響應時間:將請求重定向到當前響應最快的服務器。

在雲端擴展應用程序的 21 種方法

使用了負載均衡器的路由請求

如果採用了這個解決方案,你可以在 Azure 中啟動 20 個 VM 實例。但是,如果嘗試訪問部署在離用戶較遠的服務器上的圖像、視頻、Html 或其他靜態資源,請求太耗時,這個時候該怎麼辦?這個時候可以使用 CDN。

d. 內容交付網絡(CDN)

CDN 用於降低從服務器獲取靜態內容到用戶位置的延遲。造成該延遲的主要原因有兩個。第一個原因是用戶和服務器之間的物理距離太長。CDN 位於全球多個稱為 POP(Point-of-Presence)的位置上,因此可能存在一個比服務器更接近用戶的 CDN。第二個原因是訪問磁盤上的文件。CDN 可能使用 HDD、SSD 或 RAM 組合來緩存這些數據,具體取決於數據的訪問頻率。緩存具有生存時間(time-to-live,簡稱 TTL),表示緩存內容在某個時刻過期。

在雲端擴展應用程序的 21 種方法

使用 CDN 獲取緩存內容

CDN 緩存的是靜態內容文件。但是,如果需要緩存一些動態數據,那該怎麼辦?可以用緩存來解決這個問題。

e. 緩存

當很多對數據庫的 SQL 請求生成相同的結果時,最好可以將這些結果緩存在內存中,以確保更快的數據訪問,並減少數據庫的負載。比如,一種典型的用例是在網站主頁上為所有用戶列出排名前 10 的產品。緩存位於 RAM 中而不是硬盤上,可以保存跟 RAM 同樣多的數據。數據以鍵值對的形式存儲。緩存可以跨多區域分佈。

Azure Redis Cache 可以存儲高達 530GB 的數據,不過 Azure 沒有提供 Memcahed 緩存服務。

如果底層基礎設施出現故障,可能會發生潛在的數據丟失。Redis 為數據持久性提供了一種解決方案,而 Memcached 沒有。

當用戶在網站中搜索特定信息時,服務器會收到請求,並從數據庫獲取相關的數據。大多數請求在形式上是類似的。那麼,為什麼不緩存這些結果呢?這就是 Elasticsearch 大展身手的時候了。

f. Elasticsearch

Elasticsearch 可以用於存儲預定義搜索查詢及其結果。因為它是在內存中保存數據,所以比從數據庫中檢索這些數據要快得多。它還可以在你輸入搜索關鍵字時提供幾乎實時的搜索建議。這樣可以減少數據庫的負載,同時也減少了服務器負載,因為處理請求用時更少。Elasticsearch 可以通過 REST API 訪問,不過需要修改應用程序的代碼。

Elasticsearch 還可以用於日誌分析和實時應用程序監控。

當使用多個單體 web 應用程序副本仍然不足以或不能有效地處理所有用戶請求時,需要考慮把應用程序分成 2 個部分:web app 和 web API。

g. 前後端分離

單體架構 web 應用程序通常由 2 層組成:Web API 和前端 web app。ASP. NET MVC 應用程序就是這種情況,其中視圖和業務邏輯存在同一個服務器上。在這種情況下,服務器不僅要處理用戶獲取數據的請求,還要渲染 web 頁面以生成 HTML 內容。第 2 個任務可以通過在客戶端使用 SPA(Single Page Application,單頁面應用程序)來實現。因此,應用程序的視圖部分可以遷移到另一台單獨的服務器上。現在是由兩台服務器而不是一台服務器為用戶提供服務。

這種方法需要重寫應用程序。

把應用程序分成 2 個部分還不夠?那麼就分成多個部分,怎麼樣?現在輪到微服務出場了。

h. 微服務和容器

微服務是一種把單個應用程序作為一組小型服務而不是單個巨型應用程序來開發的方法。每個小型的服務在自己的進程中運行,而不是依賴單個進程來運行整個應用程序。典型的例子是將電子商務應用程序分成多個微服務,用於支付、前端用戶界面、電子郵件通知、管理評論和推薦。

可以把微服務視為 OOP 中的關注點分離和單一責任原則,只是這些原則作用的不是類層面,也而是組件層面。

使用這種方法,可以只擴展應用程序的特定部分,以便接收更多負載。因此,你可以有 5 個運行前端微服務的實例和 2 個運行支付微服務的實例。

這些微服務使用輕量級的機制進行通信,這些輕量級機制通常是 HTTP API。因為它們是輕量級的,在運行時不需要佔用整個 VM 實例。相反,它們可以在容器上運行。容器是 OS 虛擬化的另一種形式。與 VM 不同,容器只提供了運行應用程序所需的最少資源,比 VM 具有更好的擴展性。

這些容器可以通過像 Kubernetes 或 Swarm 這樣的編配器進行管理。

在雲端擴展應用程序的 21 種方法

將每個微服務託管在一個容器中

從單體到微服務需要對應用程序源代碼進行大量更改。要求從頭重寫整個應用程序並不罕見。

Azure 支持 Docker 容器,還支持像 Kubernetes、Service Fabric 和 Swarm 這樣的容器編配器。

藉助微服務,單體應用程序可以按照業務領域分成小塊。除了把應用程序分成更小的塊之外,我們還可以更進一步嗎?接下來,無服務器應用程序登場了。

i. Azure Function(無服務器應用程序)

無服務器應用程序是一小部分應用程序,它託管在自身的實例上。該實例受到管理,因此用戶不需要關心任何容器或 VM 的問題。它可以根據負載情況自動水平擴展。通常,可用它調整圖像的大小或處理圖像、在數據庫上啟動一項作業等操作,這些操作通常獨立於業務邏輯。

現在我們運行很多層,每一層都在不同的端點上,客戶端應用程序會問:我應該去哪裡?API 管理就是設計用來解決這個問題的。

j. Azure API Management

負載均衡器在 VM 上分發負載,而 API Management 可以在不同 API 端點或微服務上分發負載。這種分發機制考慮到了每個端點上的負載。

在雲端擴展應用程序的 21 種方法

API Management 在不同的 API 服務之間分配負載

我們把單體應用程序分成多個小模塊,這些模塊彼此間需要通信。很顯然,它們可以使用 REST web 服務。有些通信不需要使用同步的方式,既然可以不使用同步的方式,那為什麼還要等待響應呢?這個時候可以使用隊列。

k. Azure Queue Storage

隊列為軟件組件之間的通信提供了異步解決方案。在使用 REST web 服務進行通信時,所請求的服務器必須可用,否則請求就會失敗。而在使用隊列時,及時請求的服務器不可用也沒問題。請求可以在隊列中等待,當服務器稍後可用時繼續處理請求。這種方法有助於解耦不同的組件,使它們易於擴展並具有彈性。

在雲端擴展應用程序的 21 種方法

基於消息的微服務間通信

還有其他一些技術可用來減少服務器負載。除了服務器之外,還有誰可以處理數據?在某些場景中,前端也許是個不錯的選擇。

l. 把任務推送到客戶端

很多任務不需要涉及數據庫或服務器。比如,如果前端可以自行調整圖像大小,那麼還要什麼服務器呢?多年前,服務器試圖為客戶端完成所有繁重的工作,因為那時候客戶端還不夠強大。現在,情況已經不同了:擁有 8 個內核的處理器及 4GB RAM 對於一台移動設備來講已經不是什麼令人驚訝的配置了。

不使用服務器的另一種解決方案是:如果確定能獲得同樣的響應,那麼就不需要多次發出同樣的請求。在客戶端緩存 HTTP 響應是個好辦法。

m. 緩存可重複的 HTTP 請求和響應

如果有必要,可以讓瀏覽器智能地緩存 HTTP 請求及其響應。除此之外,它還為緩存的每一項數據提供 TTL(Time-To-Live)。這樣,web 應用程序只在第一次需要連接服務器,以後就從緩存中獲取響應。這不僅減少了服務器的負載,而且讓客戶端應用程序響應速度更快。該方案相對來說易於實現,因為只需要在請求中添加 HTTP 標頭。瀏覽器將解釋這些內容並負責從其自身的緩存返回數據或把請求路由到服務器。

到目前為止,我們已經解釋了擴展 web 應用程序的幾種選擇。但是,幾乎所有的應用程序都需要連接到數據庫或服務器。當應用程序的負載增加時,通常會影響到數據庫。與 web 應用程序一樣,數據庫每秒能接受的請求是有限的。不僅如此,SQL 數據庫實例能存儲的數據量也有上限。因此,需要擴展數據庫。那麼,如何擴展數據庫呢?

2. 擴展數據庫

我們用於擴展 web 應用程序的大多數原則都適用於擴展數據庫,比如垂直擴展和水平擴展、緩存和複製。我們將一一解釋它們,從簡單到複雜,逐步深入。

提高數據庫響應速度最簡單的方法是利用數據庫的內置特性。 我說的是緩存數據。

a. 緩存數據查詢

SQL 數據庫有個很好的數據緩存特性:緩衝區緩存。它允許在內存中緩存最頻繁的查詢。因此,數據訪問速度更快。

緩存查詢受限於可用內存的大小。當緩存無法為大量查詢帶來好處以及需要查詢大型表格時,我們需要儘可能快地檢索數據。這個時候需要使用索引!

b. 數據庫索引

根據 ID 從表格中檢索數據需要遍歷幾乎所有行,因為 ID 是無序主鍵。ID 為 5 的客戶信息可能位於第 7 行。如果使用了索引,它將位於第 5 行。因此,如果我們確切地知道在哪裡能夠找到所需的信息,就無需遍歷整個表。

即使使用了索引,也許還達不到所需的響應時間,可能是因為 SQL 查詢沒有經過優化,所以我們需要優化查詢。

c. 存儲過程和 ORM

性能測試顯示,存儲過程比 SQL 代碼或 ORM 的查詢更快,因為它們在數據庫中經過預編譯。這裡存在一個權衡,因為另一方面,ORM 更容易使用,而且可以處理多個數據庫,甚至可以優化 SQL 查詢。我們可以把兩者混合起來使用以獲得最佳效果。

如果達到了單個數據庫的上限,那麼為什麼不創建多個數據庫實例呢?複製可以基於多種方式。我們從基於讀或讀寫操作開始吧。

d. 複製數據庫

SQL 查詢不是讀數據就是寫數據。那麼工程師可能會問:為什麼不在一個數據庫中寫入,然後從另一個數據庫讀出呢?這樣就可以通過在兩個數據庫而不是一個數據庫實現負載均衡,可以將負載減少一半。寫入數據庫將負責更新讀取數據庫,這樣兩個數據庫就擁有幾乎相同的數據。

在 web 應用程序中,當我們想做的不只是複製實例時,可以把應用程序分解成若干小組件。那麼我們可以把同樣的邏輯應用在數據庫上嗎?複製數據庫的另一個方式是基於拆分數據表。這叫做分區或分片。我們從分區開始講。

e. 垂直拆分(分區)

分區把一張表分成多張表,每張表擁有的列更少一些。包含 20 列數據的客戶表格將被分成兩張或更多張表。第一張表將擁有列 1 到列 7 的數據,而第二張表將擁有列 8 到列 20 的數據。當然,每張表都包含了主鍵,用於連接這兩張表。如果只需要前 7 列的數據,那麼這種拆分方式就會非常有用。由於它返回的數據列更少,所以運行時間也更短。這兩張表可以放在同一個數據庫中或者兩個獨立的數據庫中。SQL 數據庫為此提供了一些特性,能夠識別從哪個分區獲取需要的數據。

在雲端擴展應用程序的 21 種方法

垂直分割表格

在考慮使用垂直分區時要慎重,因為如果要對數據進行分析,需要連接來自多個分區的數據。

我們通過拆分列對表格進行垂直拆分。那麼,我們能否按行來拆分表格?這就是分片。

f. 水平拆分(分片)

分片把一張表分成多張表,每張表擁有相同數量的列,但更少的行。比如,客戶表可以分成 5 張更小的表,每張表代表了一組客戶。了解客戶數據所在的位置有助於將查詢定向到正確的分區,這樣就可以處理更少的行。較小的表可以位於同一個或不同的 SQL 實例中,與垂直分區一樣。水平分片和垂直分區可以混合使用。

在雲端擴展應用程序的 21 種方法

水平拆分表

表應該進行分區,以便查詢引用儘可能少的表。否則,過多的合併查詢(在查詢時用於邏輯合併表)會影響性能。

到目前為止,我們已經看到基於列或行的表拆分。但是,我們能否把表分成一組表?我們來看看 DDD。

g. 領域驅動設計(DDD)

我們根據上下文或領域把 web 應用程序分成更小的微服務。同樣的道理也可以應用於 DDD。這樣,每個領域有自己的一組表:用於支付、評價的表,等等。

設想一下,現在每個微服務在同一個容器中都有自己的領域數據表。

DDD 的目標不是擴展數據庫,但應用 DDD 就會得到這樣的結果。但如果不是在項目開始階段就進行設計,那麼,在後續可能需要進行大量的源碼修改。

如果 SQL 數據庫還不足以處理所有的負載呢?那麼,為什麼不試試 NoSQL?

h. Azure Cosmos DB 和 Azure Tables(NoSql 數據庫)

SQL 數據庫以 schema 和表之間的關係為基礎,這是導致其無法無限擴展的核心問題。另一方面,NoSQL 數據庫用鍵值對的形式存儲數據,不需要 schema,也不需要表間關係。因此,表可以進行無限的水平拆分。

NoSQL 消除了表間關係,但仍然可以做些調整來建立關係,不過不推薦這麼做。

結語

在雲端擴展應用程序不只是架構師的責任,開發人員也需要考慮無狀態問題,數據庫管理員需要考慮數據庫分區。其他需要考慮重要事項:指標和分析。因為這些能夠告訴我們是否需要進行垂直擴展還是水平擴展,以及哪些東西需要擴展。

英文原文 https://medium.com/devopslinks/scaling-applications-in-the-cloud-52bb6dfbac4e

原文 : InfoQ

相關閱讀

免责声明:本文内容来源于InfoQ,已注明原文出处和链接,文章观点不代表立场,如若侵犯到您的权益,或涉不实谣言,敬请向我们提出检举。