如何實現可擴展的架構?
作者 | Miloslav Voloskov
譯者 | 平川
策劃 | 萬佳
本文爲實現可擴展架構提出了幾個原則:使用合適的工具。不要把寫入優先和讀取優先數據庫弄混了。什麼東西都配置多份。要實現多份配置,就必須讓它們保持無狀態。不要讓後端完成數據庫的工作,那樣總是更慢。
可擴展性被認爲是一個很難解決的問題。人們總是把它看成是一種神奇的東西,是用神祕而特殊的工具完成的,只有身價百萬的大塊頭才能使用。這當然不是真的。其實,那並沒有什麼神奇之處——那也不過是用普通編程語言編寫的普通代碼。
首先,要針對工作選擇合適工具。你已經看過基準測試了,你知道有些語言在某些方面表現得更好。有些數據庫的讀取速度更快,而有些數據庫的寫入速度更快。即使你已經爲任務選擇了合適的技術棧,一臺服務器也是不夠的。這就是有趣的地方。當然,你可以直接從不同的 AWS 服務級別中進行選擇。但是,如果想知道其中的原理,你就應該知道如何在裸金屬上實現可擴展的設置。
1 基本原則
選擇恰當的工具
不同的編程語言適用於不同的任務。
例如,Python 有非常豐富的語法糖,非常適合處理數據,而且代碼簡短而富有表現力。但爲了實現這一點,它需要運行在解釋器上,在默認情況下,這比編譯後在裸金屬上運行的 Go 或 C 是要慢的。
NodeJS 的外部工具可能是最豐富的,但它是單線程的。要在多核機器上運行 NodeJS,必須使用像 PM2 這樣的東西,但這樣的話,就必須保持代碼是無狀態的。
http://pm2.keymetrics.io/?fileGuid=gr8wsimng4sTPe0C
數據庫也是一樣。SQL 提供了圖靈完備性來查詢和處理數據,但這是有代價的——沒有緩存,SQL 幾乎總是比 NoSQL 慢。
除此之外,數據庫通常是讀取優先或寫入優先的。這就意味着,它們中的一些在寫入數據時速度更快,而另一些在大量讀取時性能更佳。
例如,對於需要大量寫入、偶爾讀取的分析及其他任務,你可能想要選擇 “寫入優先” 的數據庫,如 Cassandra。
對於顯示新聞這樣的讀取優先任務,最好使用像 MongoDB 這樣的東西。
如果兩者都需要,就安裝兩個數據庫!這不是不行。這不會造成什麼破壞。事情就應該這樣做。
多服務器
當一臺計算機不夠用的時候,可以用兩臺。當兩臺不夠用的時候,可以買三臺,以此類推。
但也有一個陷阱:從 1 到 2 比從 2 到 3 或從 10 到 20 要難得多。
要使用多臺計算機,後端應該是無狀態的。這意味着你必須將所有數據都存儲到數據庫中,而後端不保存任何數據。這就是函數式語言在後端如此流行的原因,這也是 Scala 被髮明的原因。函數代碼默認是無狀態的。
無論如何,不同服務器的行爲應該完全相同。如果你有大量的有狀態服務器,那麼根據定義,對相同的輸入,它們很容易返回不同的數據作爲響應,因爲有兩個事實來源:數據庫和服務器狀態。相信我,你不會想讓這種事情發生的。
儘快實現無狀態。最好從一開始就選擇無狀態。如果你在使用 NodeJS 和 PM2,如果你想讓 PM2 幫你增加運行時以實現負載均衡,那你就必須讓代碼保持無狀態。
負載均衡器會將請求重新路由到最空閒的服務器。顯然,對於相同的請求,服務器應該提供完全相同的響應。這就是我們轉向無狀態的原因。對 NodeJS 來說,PM2 是一個很好的負載均衡選項。如果你用的不是 Node,就選擇 Nginx。
會話?把它們保存在 Redis 中,並讓所有服務器都可以訪問。
緩存和速率限制
想象一下,每 100 毫秒針對每個用戶做同樣的計算。這將使服務器很容易受到 Slashdot 效應的影響——基本上只是用戶訪問數據就會導致 DDOS。
增加緩存中間件。只有第一個用戶將觸發數據查詢,其他所有用戶將直接從 RAM 接收完全相同的數據。
這也有缺點——默認情況下,數據會過期。通常,緩存中間件允許設置緩存重置時間,數據最終會刷新。
考慮用戶和他們的需求,配置相應的緩存。永遠不要緩存用戶輸入。只有服務器輸出應該被緩存。
Varnish 是一個很好的 HTTP 響應緩存選項,所以它可以用於任何後端。
https://varnish-cache.org/?fileGuid=gr8wsimng4sTPe0C
即使有了緩存,每 10 毫秒就會出現不同的請求,也可能會導致服務器宕機,因爲服務器會爲它們計算不同的響應。這就是爲什麼你需要一個速率限制器——如果距離上次請求的時間不夠長,正在進行的請求將被拒絕。這將使你的服務器保持活躍。
劃分職責
如果你正在使用 SQL 數據庫,並且仍然使用後端計算外鍵,那麼你沒有充分利用數據庫的能力。只需設置記錄之間的關係並允許數據庫爲你計算外鍵——查詢規劃器總是比後端更快。
後端應該有不同的職責:哈希、從數據和模板構建網頁、管理會話等等。
對於任何與數據管理或數據模型相關的內容,將其作爲存儲過程或查詢移到數據庫中。
大數據量
即使是使用數據庫集羣,最大容量也受限於服務器的主板。你不能只是把無限多的硬盤放在那裏。如果想要無限增長,除了使用分佈式數據庫之外,沒有其他選擇。它將數據存儲在不同的服務器上,最大容量接近所有服務器容量的總和。如果存儲空間不足,只需添加另一臺服務器即可。
通過主從複製,你可以將 DB 加倍並實現負載均衡,但容量不會無限增長。
可能存在的瓶頸
-
單線程、有狀態、不可擴展的服務器。爲了實現負載均衡及運行多臺服務器,代碼必須是無狀態的。
-
服務器做數據庫的工作。將任何與數據相關的工作移到數據庫中。
-
單數據庫實例。實現數據庫負載均衡,請選用集羣。
-
把讀取優先和寫入優先搞混了。分析常見任務,有針對性的使用不同類型的數據庫。
-
距離客戶端太遠。請使用 CDN。
2 設置舉例
小貓
這是你一個晚上就可以在 LAMP 技術棧上構建的基本設置。它是有狀態的——它在內存中存儲會話和其他雜七雜八的東西。你猜對了,它根本無法擴展。但是,它仍然非常適合小型週末項目。
-
數據:GB 級
-
用戶:幾千
-
瓶頸:可用性。單服務器,很容易受 Slashdot 效應影響
-
工具:常規的 LAMP 技術棧
大貓
我們添加了緩存。雖然速度提升了,但由於架構是有狀態的,所仍然不可擴展。當你的週末項目用戶增加時,你應該這樣做。
-
數據:GB 級
-
用戶:幾萬
-
瓶頸:有狀態服務器。即使有了緩存,服務器仍是不可擴展的
-
工具:MongoDB、Express 作爲速率限制器和內存緩存
獵豹
這是可擴展的!你可以擁有任意數量的服務器。現在,你可以處理所有可能導致 “大貓” 宕機的請求,但數據庫仍然是運行單個實例,必須處理所有請求。儘管如此,它還是非常適合小型項目、電子商店或類似的東西。
-
數據:TB 級
-
用戶:十幾萬
-
瓶頸:單數據庫。使用函數式語言,服務器是可擴展的。但是單個 DB 可能無法處理大量的請求
-
工具:Go、Redis 緩存、MongoDB
老虎
這個架構速度很快,而且可擴展。看它有多漂亮。DB 和後端都做了負載均衡。這裏的瓶頸是,當你運行單個服務器或數據中心時,海外用戶可能會面臨高延遲,因爲他們距離很遠。但是,這種設置仍然可以應對許多用戶,非常適合新聞網站。
-
數據:數百 TB
-
用戶:上百萬
-
瓶頸:距離。服務器速度很快,但如果用戶距離很遠,速度也可能會慢
-
工具:Go、Redis + Cassandra + MongoDB
獅子
這是一個 CDN——一種完全不同的東西。你在世界各地有多臺服務器,它們可以像主服務器一樣爲請求提供服務。這不像緩存,它們是全功能的。
來自不同大洲的用戶通過 DNS 進行隔離。
儘管服務器速度很快,但你仍然受限於一臺服務器的容量。你的數據庫是主數據庫的副本,因此你受限於主數據庫的容量。
這非常適合託管提供商、大型電子商務之類的東西。
-
數據:數百 TB
-
用戶:上千萬
-
瓶頸:大數據量。使用主從複製,無法處理大數據量,你受限於一臺 DB 服務器的容量
-
工具:同上,但 MongoDB 是集羣
劍齒虎
這是終極形式。有了 Riak 這樣的圖形數據庫,容量將不再受限。當存儲資源不足時,你只需購買一個新的存儲服務器並將其添加進去。
非常適合創建像谷歌或 Facebook 那樣的應用。
-
數據:無限
-
用戶:全球用戶
-
瓶頸:價格。其成本就像太空項目
-
工具:Go、Riak
3 總結
我們回顧了幾乎每類項目的一些最常見的設置。不一定非要使用上述設置——根據自己的需要進行設計。只要記住,每個工具都有它的用途,務必選擇適合你的工作的合適工具。
保證可擴展,保證無狀態!
原文鏈接:
https://mvoloskov.hashnode.dev/scalable-architecture-without-magic-and-how-to-build-it-if-youre-not-google?fileGuid=gr8wsimng4sTPe0C
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/9_EueIQxrpPzvOYmvwczzw