如何從 1 個用戶擴展到 100 個用戶

許多初創公司都經歷過這樣的情況:每天都有大量新用戶註冊,開發團隊努力維持服務運行。

這是一個很好的問題,但是網絡上幾乎沒有關於如何小心地將 Web 應用程序從無擴展到擁有數十萬用戶的明確信息。 通常有火災解決方案或瓶頸解決方案(通常兩者都有)。 因此,人們使用相當陳詞濫調的技術將他們的業餘項目擴展到真正嚴肅的事情。

讓我們嘗試過濾信息並寫下基本公式。 我們將逐步將新照片共享網站 Graminsta 的用戶規模從 1 名擴大到 100 名。

我們來寫一下當觀眾增加到10、100、1000、10和000人時需要採取哪些具體行動。

1 個用戶:1 台機器

幾乎每個應用程序,無論是網站還是移動應用程序,都具有三個關鍵組件:

  • API
  • 數據庫
  • 客戶端(移動應用程序或網站本身)

數據庫存儲持久數據。 API 為針對此數據以及圍繞此數據的請求提供服務。 客戶端向用戶發送數據。

我得出的結論是,如果從架構的角度來看,客戶端和 API 實體完全分離,那麼談論擴展應用程序就會容易得多。

當我們第一次開始構建應用程序時,所有三個組件都可以在同一服務器上運行。 在某種程度上,這類似於我們的開發環境:一名工程師在同一台機器上運行數據庫、API 和客戶端。

理論上,我們可以將其部署在雲中的單個 DigitalOcean Droplet 或 AWS EC2 實例上,如下所示:
如何從 1 個用戶擴展到 100 個用戶
話雖如此,如果網站上有多個用戶,那麼分離數據庫層幾乎總是有意義的。

10 個用戶:將數據庫移動到單獨的級別

將數據庫拆分為 Amazon RDS 或 Digital Ocean Managed Database 等託管服務將在未來很長一段時間內為我們提供良好的服務。 它比在單台計算機或EC2 實例上自託管要貴一些,但通過這些服務,您可以獲得許多開箱即用的有用的面向未來的擴展:多區域冗餘、只讀副本、自動備份等等。

這就是系統現在的樣子:
如何從 1 個用戶擴展到 100 個用戶

100 個用戶:將客戶端移至單獨的級別

幸運的是,我們的第一批用戶非常喜歡我們的應用程序。 流量變得更加穩定,因此是時候將客戶端移至單獨的級別了。 應當指出的是 分離 實體是構建可擴展應用程序的一個關鍵方面。 當系統的某一部分接收更多流量時,我們可以對其進行分區,以根據特定流量模式控制服務的擴展方式。

這就是為什麼我喜歡將客戶端與 API 分開。 這使得我們很容易考慮為多個平台進行開發:網絡、移動網絡、iOS、Android、桌面應用程序、第三方服務等。它們都只是使用相同 API 的客戶端。

例如,現在我們的用戶最常要求發布移動應用程序。 如果將客戶端和 API 實體分開,這會變得更容易。

這樣的系統是這樣的:

如何從 1 個用戶擴展到 100 個用戶

1000 個用戶:添加負載均衡器

事情在好轉。 Graminsta 用戶上傳的照片越來越多。 註冊數量也在不斷增長。 我們唯一的 API 服務器很難跟上所有流量。 需要更多的鐵!

負載均衡器是一個非常強大的概念。 關鍵思想是我們在 API 前面放置一個負載均衡器,它將流量分配到各個服務實例。 這就是我們水平擴展的方式,這意味著我們使用相同的代碼添加更多服務器,從而增加我們可以處理的請求數量。

我們將在 Web 客戶端和 API 前面放置單獨的負載均衡器。 這意味著您可以運行多個運行 API 代碼和 Web 客戶端代碼的實例。 負載均衡器會將請求定向到負載較少的服務器。

在這裡我們得到了另一個重要的優勢——冗餘。 當一個實例失敗(可能過載或崩潰)時,我們剩下的其他實例仍在響應傳入的請求。 如果單個實例正在運行,那麼一旦崩潰,整個系統就會崩潰。

負載均衡器還提供自動縮放功能。 我們可以將其配置為在峰值負載之前增加實例數量,並在所有用戶都睡眠時減少實例數量。

使用負載均衡器,API 級別幾乎可以無限擴展,只需隨著請求數量的增加添加新實例即可。

如何從 1 個用戶擴展到 100 個用戶

筆記。 目前,我們的系統與 AWS 上的 Heroku 或 Elastic Beanstalk 等 PaaS 公司提供的開箱即用的系統非常相似(這就是它們如此受歡迎的原因)。 Heroku 將數據庫放在單獨的主機上,管理自動擴展負載均衡器,並允許您將 Web 客戶端與 API 分開託管。 這是在早期項目或初創公司中使用 Heroku 的一個重要原因 - 您可以立即獲得所有基本服務。

10 個用戶:CDN

也許我們從一開始就應該這樣做。 處理請求和接受新照片開始給我們的服務器帶來太大壓力。

在此階段,您需要使用雲服務來存儲靜態內容 - 圖像、視頻等(AWS S3 或 Digital Ocean Spaces)。 一般來說,我們的 API 應避免處理諸如提供圖像和將圖像上傳到服務器之類的事情。

雲託管的另一個優勢是 CDN(AWS 將此附加組件稱為 Cloudfront,但許多雲存儲提供商都提供開箱即用的服務)。 CDN 自動將我們的圖像緩存在世界各地的各個數據中心。

儘管我們的主數據中心可能位於俄亥俄州,但如果有人從日本請求圖像,雲提供商將製作副本並將其存儲在日本數據中心。 下一個在日本請求此圖像的人會更快地收到它。 當我們處理大文件(例如照片或視頻)時,這一點非常重要,這些文件需要很長時間才能下載並在全球範圍內傳輸。

如何從 1 個用戶擴展到 100 個用戶

100 個用戶:擴展數據層

CDN 幫了大忙:流量正在全速增長。 正如他們所說,著名視頻博主馬維德·莫布里克剛剛在我們註冊並發布了他的“故事”。 感謝負載均衡器,API 服務器上的 CPU 和內存使用率保持在較低水平(運行 XNUMX 個 API 實例),但我們開始出現大量請求超時……這些延遲從何而來?

深入研究一下指標,我們發現數據庫服務器上的 CPU 負載率為 80-90%。 我們已經到了極限。

擴展數據層可能是方程式中最困難的部分。 API 服務器提供無狀態請求,因此我們只需添加更多 API 實例即可。 鼻子 多數 數據庫無法做到這一點。 我們將討論流行的關係數據庫管理系統(PostgreSQL、MySQL 等)。

緩存

提高數據庫性能的最簡單方法之一是引入一個新組件:緩存層。 最常見的緩存方法是內存中的鍵值記錄存儲,例如 Redis 或 Memcached。 大多數雲都有這些服務的託管版本:AWS 上的 Elasticache 和 Google Cloud 上的 Memorystore。

當服務多次重複調用數據庫以檢索相同信息時,緩存非常有用。 本質上,我們只訪問數據庫一次,將信息存儲在緩存中,並且不會再次觸及它。

例如,在我們的 Graminsta 服務中,每次有人訪問明星 Mobrik 的個人資料頁面時,API 服務器都會從數據庫中查詢其個人資料中的信息。 這種情況一再發生。 由於 Mobrik 的配置文件信息不會隨每個請求而改變,因此非常適合緩存。

我們將數據庫中的結果按key緩存在Redis中 user:id 有效期為30秒。 現在,當有人訪問 Mobrik 的個人資料時,我們首先檢查 Redis,如果數據存在,我們只需直接從 Redis 傳輸即可。 現在,對網站上最受歡迎的配置文件的請求實際上不會加載我們的數據庫。

大多數緩存服務的另一個優點是它們比數據庫服務器更容易擴展。 Redis內置了Redis集群模式。 類似於負載均衡器1,它允許您將 Redis 緩存分佈在多台機器上(如果需要,可以是數千台服務器)。

幾乎所有大型應用程序都使用緩存;它絕對是快速 API 不可或缺的一部分。 更快的查詢處理和更高效的代碼都很重要,但如果沒有緩存,幾乎不可能將服務擴展到數百萬用戶。

讀取副本

當數據庫的查詢數量大幅增加時,我們還能做的另一件事就是在數據庫管理系統中添加只讀副本。 通過上述託管服務,只需單擊一下即可完成此操作。 只讀副本將在主數據庫中保持最新狀態,並且可用於 SELECT 語句。

現在這是我們的系統:

如何從 1 個用戶擴展到 100 個用戶

下一步

隨著應用程序不斷擴展,我們將繼續分離服務以獨立擴展它們。 例如,如果我們開始使用 Websockets,那麼將 Websockets 處理代碼拉入單獨的服務中是有意義的。 我們可以將其放置在我們自己的負載均衡器後面的新實例上,該負載均衡器可以根據開放的 Websockets 連接進行擴展和縮減,而不管 HTTP 請求的數量如何。

我們還將繼續對抗數據庫級別的限制。 到了這個階段,就該研究數據庫分區和分片了。 這兩種方法都需要額外的開銷,但允許您幾乎無限地擴展數據庫。

我們還想安裝 New Relic 或 Datadog 等監控和分析服務。 這將幫助您識別緩慢的查詢並了解哪裡需要改進。 隨著規模的擴大,我們希望專注於尋找瓶頸並消除它們——通常使用前幾節中的一些想法。

來源

這篇文章的靈感來自於其中之一 我最喜歡的關於高可擴展性的帖子。 我想讓這篇文章更具體地介紹項目的初始階段,並將其與一個供應商分開。 如果您對此主題感興趣,請務必閱讀。

雪域

  1. 雖然在跨多個實例的負載均衡方麵類似,但 Redis 集群的底層實現與負載均衡器有很大不同。 [返回]

如何從 1 個用戶擴展到 100 個用戶

來源: www.habr.com

添加評論