1. 系統架構基礎 1.1. 幾乎所有大規模系統都是從小規模開始,在成功路上逐漸發展壯大 1.2. 通信使用標準的應用層網路協議,通常是HTTP 1.3. Java EE和Java的Spring框架 1.4. Python的Flask 1.5. 隨著應用程式的特性變得更加豐富,單體應用程式的複雜性往 ...
1. 系統架構基礎
1.1. 幾乎所有大規模系統都是從小規模開始,在成功路上逐漸發展壯大
1.2. 通信使用標準的應用層網路協議,通常是HTTP
1.3. Java EE和Java的Spring框架
1.4. Python的Flask
1.5. 隨著應用程式的特性變得更加豐富,單體應用程式的複雜性往往會增加
-
1.5.1. 所有API處理程式都內置在同一個伺服器代碼中,最終會讓快速修改和測試變得困難
-
1.5.2. 所有API實現都在同一個應用服務中運行,執行足跡可能變得極其繁雜
-
1.5.3. 如果請求的負載壓力保持較低的水平,則此應用程式架構足夠了,服務足以處理請求並保持低延遲
-
1.5.4. 如果請求負載持續增長,則意味著延遲增加,因為服務沒有足夠的CPU和記憶體來同時處理大量的併發請求,請求將需要等待更長的時間
-
1.5.4.1. 單台伺服器已經超載,成了性能瓶頸
-
1.5.4.2. 擴展策略通常是“擴展”應用服務的硬體
1.5.4.2.1. 為應用程式提供翻倍的CPU和可用記憶體
1.5.4.2.2. 擴展硬體要花更多的錢,但也物有所值,擴展後的系統滿足了你的實際需求
-
1.6. 不可避免的是,對於許多應用程式,無論你擁有多少CPU和多少記憶體,負載都將增長到超出單個伺服器節點所能服務的範圍
1.7. 服務是獨立部署的進程,它們使用HTTP等遠程通信機制進行通信
- 1.7.1. 在架構領域,服務最接近於面向服務的架構(SOA)模式中的服務,那是一種用於構建分散式系統的著名架構方法,它更現代的演變圍繞著微服務展開
2. 水平擴展
2.1. 水平擴展是指依靠架構複製服務,併在多個伺服器節點上運行多個服務副本的能力
-
2.1.1. 來自客戶端的請求被分佈在不同的副本
-
2.1.2. 理論上講,如果有N個服務副本和R個請求,則每個伺服器節點處理R/N個請求
-
2.1.3. 這種簡單的策略增加了應用程式的存儲能力和計算能力,從而提升了可擴展性
-
2.1.4. 理論上,你可以不斷添加新的(虛擬)硬體和服務來處理增加的請求負載,並持續保持低延遲
2.2. 負載均衡器
-
2.2.1. 所有用戶請求都發送到負載均衡器,由它來決定處理請求的目標服務副本
-
2.2.2. 所有策略的核心目的都是讓不同的資源同等繁忙
-
2.2.3. 負載均衡器還負責將來自服務副本的響應轉發回客戶端
-
2.2.4. 大多數負載均衡器都屬於反向代理的互聯網組件
- 2.2.4.1. 它們控制著客戶端請求對伺服器資源的訪問
-
2.2.5. 扮演著中介的角色,反向代理為請求添加了額外的網路跳躍點,它們需要將延遲控制到極低,才能最大限度地減少引入的開銷
2.3. 無狀態服務
-
2.3.1. 為了使負載平衡有效並均勻地分發請求,負載均衡器必須可以自由地將來自同一客戶端的連續請求發送到不同的服務實例進行處理
- 2.3.1.1. 意味著服務中的API實現不得保留與單個客戶端會話相關聯的任何知識或狀態
-
2.3.2. 使用的是無狀態服務,擴展過程中無須更改代碼,你只需要為部署的硬體付費,所以這是一種相對便宜的策略
2.4. 如果其中一項服務副本宕機,那麼它正在處理的請求將丟失,而宕機的服務副本不管理會話狀態,請求可以簡單地由客戶端重新發出,發送到其他服務實例進行處理
- 2.4.1. 應用程式對服務軟體和硬體中的故障具有彈性恢復能力,從而提高了應用程式的可用性
2.5. 隨著添加服務實例越來越多,請求處理能力會增長,理論上會無限增長
-
2.5.1. 單一資料庫提供低延遲查詢響應的能力將會減弱
- 2.5.1.1. 資料庫成了性能瓶頸
-
2.5.2. 如果請求的到達速度比處理速度快,一些系統組件會因為資源耗盡而過載和失敗,客戶端便會收到異常和請求超時
3. 使用緩存擴展資料庫
3.1. 對於資料庫伺服器,增加CPU數量以及記憶體和磁碟的容量有助於擴展系統
3.2. 水平擴展對於資料庫也是一種常見的擴展策略
3.3. 要維持大型資料庫高效和快速運行,往往需要技術嫻熟的資料庫管理員持續地看護和關註
- 3.3.1. 資料庫管理員是寶貴的人才,值得你珍惜,他們可以讓你的應用服務保持較快的響應速度
3.4. 在擴大規模的同時,一種高效的方法是儘可能低頻地從服務中查詢資料庫,可以在水平擴展的服務層中使用分散式緩存來實現
- 3.4.1. 將近期檢索和經常訪問的資料庫結果緩存存儲在記憶體中,既可以快速檢索它們,也不會給資料庫帶來負擔
3.5. 對於經常讀取且很少更改的數據,可以將處理邏輯改成先檢查分散式緩存
-
3.5.1. Redis
-
3.5.2. memcached
3.6. 緩存技術本質上是使用非常簡單的API進行的分散式鍵值存儲
3.7. 如果你需要的數據在緩存中,那麼在快速網路上,數據讀取的延遲可以是毫秒級的
- 3.7.1. 比查詢共用資料庫實例要便宜許多,而且無須爭奪稀缺的資料庫連接
3.8. 引入緩存層需要你將處理邏輯修改為檢查緩存的數據
-
3.8.1. 如果內容不在緩存中,那麼代碼仍需查詢資料庫並將結果載入到緩存中,同時將其返回給調用者
-
3.8.2. 還需要決定何時刪除緩存數據或使緩存結果無效
-
3.8.3. 你的行動方案取決於數據的性質以及你的應用程式對向客戶端提供過時(也稱為陳舊)結果的容忍度
3.9. 一個設計良好的緩存方案對於擴展系統是無價之寶
- 3.9.1. 緩存非常適用於低頻更改且經常訪問的數據
4. 分散式資料庫
4.1. 分散式SQL存儲
-
4.1.1. 分散式SQL資料庫允許組織機構將數據存儲在多個磁碟,由多個資料庫引擎副本查詢磁碟上的數據,從而實現相對無縫的擴展
-
4.1.2. 多個資料庫引擎在邏輯上對應用程式顯示為單個資料庫,最大限度地減少了代碼更改
4.2. 分散式NoSQL存儲
-
4.2.1. 每個節點都有自己的本地附加存儲
-
4.2.2. 領先的產品有Cassandra、MongoDB和Neo4j
4.3. 分散式資料庫也提升了可用性
- 4.3.1. 允許複製每個數據存儲節點,如果一個節點宕機了或因網路問題而無法訪問,那麼可以使用另一個數據副本
4.4. 雲服務提供商的服務
-
4.4.1. 部署自己的虛擬資源,自行構建、配置和管理分散式資料庫伺服器
-
4.4.2. 使用雲托管資料庫
4.5. 有一類大數據軟體架構可以解決在大數據集合中出現的問題,其中最典型的是數據再處理
5. 多處理層
5.1. 你需要擴展的任何實際系統都包含許多服務,通過服務間的交互協作來處理請求
5.2. 通過將應用程式分解為多個獨立的服務,可以根據服務需求對單個服務進行擴展
6. 提高系統響應能力
6.1. 大多數客戶端應用程式請求都需要響應
6.2. 從請求發送到結果接收之間的時間間隔就是請求的響應時間
6.3. 你可以使用緩存,直接返回預先計算好的響應結果來減少響應時間,但仍然有許多請求會涉及資料庫訪問
6.4. 有部分更新請求無須將數據完全持久化到資料庫中便可以成功響應
6.5. 分散式排隊平臺可以可靠地將數據從一個服務發送到另一個服務,但並非總是以先進先出(FIFO)方式
-
6.5.1. 排隊平臺都提供非同步通信
-
6.5.2. 生產者服務將消息寫入隊列後,隊列充當臨時存儲,消費者服務從隊列中刪除消息並對資料庫進行必要的更新
- 6.5.2.1. 對於不需要立即獲得寫入操作結果的場景,應用程式都可以使用該方法來提高響應能力,從而提升可擴展性
6.6. 將消息寫入隊列通常比寫入資料庫快得多,請求能夠更快地被成功確認
-
6.6.1. 數據最終會被持久化
- 6.6.1.1. 通常來說用時最多幾秒鐘,但採用這種設計的用例能夠適應更長的延遲而不影響用戶體驗
7. 系統和硬體的可擴展性
7.1. 如果服務和數據運行在存儲不足的硬體上,那麼即使是最精雕細琢的軟體架構和代碼,其可擴展能力也會受到限制
7.2. 在特定情況下,升級CPU內核數量和可用記憶體大小並不能提升系統的可擴展性
7.3. 阿姆達爾(Amdahl)定律
-
7.3.1. 如果只有5%的代碼串列執行,其餘的並行執行,那麼內核上限為2048個,超過了基本上沒有效果
-
7.3.2. 如果50%的代碼串列執行,其餘的並行執行,那麼內核上限為8個,超過了基本上沒有效果