系統設計30個核心技能,每一個開發者都必須掌握
環境:SpringBoot3.4.2
1. 簡介
系統設計會讓人感到不知所措,尤其是當你剛剛開始工作,不知從何下手的時候。
但是,一旦你了解了核心概念和構建模塊,無論是準備面試還是在工作中設計可擴展系統,它都會變得不再那么可怕。
在本文中,將帶你了解每個開發人員都應該知道的 30 個最核心的系統設計概念(技能)。
圖片
2.實戰案例
2.1 Client-Server架構
你所使用的幾乎所有網絡應用程序都是基于這一簡單而強大的概念而構建的,即客戶端-服務器架構。
圖片
一邊是客戶端,可以是網絡瀏覽器、移動應用程序或任何其他前端應用程序。
另一側是服務器 — 一臺持續運行的機器,等待處理接收到的請求。
客戶端發送存儲、檢索或修改數據的請求。
服務器接收請求,處理請求,執行必要的操作,然后發回響應。
這聽起來很簡單,但有一個大問題: 客戶端如何知道服務器在哪里?
2.2 IP地址
客戶端并不能神奇地知道服務器在哪里,它需要一個地址來定位并與之通信。
在互聯網上,計算機通過 IP 地址相互識別,IP 地址就像服務器的電話號碼。
圖片
每一臺公開部署的服務器都擁有一個唯一的IP地址。當客戶端想要與某個服務進行交互時,它必須將請求發送到正確的IP地址。
但這里存在一個問題:
- 當我們訪問一個網站時,我們并不會輸入它的IP地址——我們只需輸入網站名稱即可。
- 我們無法期望用戶(甚至系統)記住每個所連接服務的那一串隨機數字形式的IP地址。
- 而且,如果我們將服務遷移到另一臺服務器上,其IP地址可能會發生變化——這會導致所有直接連接失效。
2.3 DNS
我們不再依賴難以記憶的IP地址,而是使用對人類更加友好的方式:域名。
但是,我們需要一種方法將域名映射到其對應的IP地址。
這時,DNS(即域名系統)就派上用場了。它將易于記憶的域名(如www.pack.com)映射到它們對應的IP地址。
圖片
以下是幕后發生的情況:
當你在瀏覽器中輸入 pack.com 時,你的計算機會向 DNS 服務器詢問該域名對應的 IP 地址。
一旦 DNS 服務器返回了 IP 地址,你的瀏覽器就會使用該地址與服務器建立連接并發出請求。
你可以使用 ping 命令查找任何域名的 IP 地址。只需打開終端,輸入 ping 后跟域名即可。該命令將返回當前分配給該域名的 IP 地址。
2.4 代理 / 反向代理
當你訪問一個網站時,您的請求并不總是直接進入服務器,有時會先通過代理或反向代理。
代理服務器是設備和互聯網之間的中間人。
圖片
當你請求訪問一個網頁時,代理會將您的請求轉發到目標服務器,檢索響應并將其發送給你。
代理會隱藏你的 IP 地址,使你的位置和身份保密。
反向代理則相反。它攔截客戶端請求,并根據預定義的規則將請求轉發給后端服務器。
圖片
允許直接訪問服務器會帶來安全風險,使服務器面臨黑客和 DDoS 攻擊等威脅。
反向代理通過充當受控入口點來控制傳入流量并隱藏服務器 IP,從而降低這些風險。
它還可以充當負載平衡器,在多個服務器之間分配流量。
2.5 延遲
每當客戶端與服務器通信時,總會出現一些延遲。造成這種延遲的最大原因之一就是物理距離。
圖片
例如,如果我們的服務器在紐約,但印度的用戶發送了一個請求,那么數據就必須穿越半個地球,然后響應也必須經過同樣漫長的旅程才能返回。
這種往返延遲稱為延遲,即數據在客戶端和服務器之間傳輸所需的總時間。高延遲會讓應用程序感覺緩慢、反應遲鈍。
減少延遲的方法之一是在全球多個數據中心部署我們的服務。
這樣,用戶就可以連接到最近的服務器,而不用等待數據在全球范圍內傳輸。
建立連接后,客戶端和服務器如何進行實際通信?
2.6 HTTP / HTTPS
每次訪問網站時,瀏覽器和服務器都會使用一套名為 HTTP(超文本傳輸協議)的規則進行通信。
這就是為什么大多數 URL 都以 http:// 或其安全版本 https:// 開頭。
圖片
工作原理:
客戶端向服務器發送請求。該請求包含一個請求頭(其中包含請求類型、瀏覽器類型和 Cookie 等詳細信息),有時還包含請求體(用于攜帶額外數據,如表單輸入內容)。
服務器處理該請求,并返回一個 HTTP 響應——如果一切正常,則返回所請求的數據;如果出現問題,則返回錯誤信息。
HTTP 存在一個嚴重的安全缺陷,它以明文形式發送數據。這是一個嚴重的問題,特別是對于密碼、信用卡詳細信息和個人數據等敏感信息而言。
因此,現代網站都改用 HTTPS(超文本傳輸安全協議)。HTTPS 使用 SSL/TLS 對所有數據進行加密,確保即使有人攔截了請求,也無法讀取或篡改請求內容。
不過,客戶端和服務器并不會直接交換原始的 HTTP 請求和響應。
HTTP 只是一種數據傳輸協議,但它并未定義:
- 請求應如何構建
- 響應應采用何種格式
- 或者不同客戶端應如何與服務器進行交互
這時,API(即應用程序編程接口)就派上用場了。
2.7 APIs
將應用程序接口視為一個中間人,它允許客戶端(如網絡和移動應用程序)與服務器通信,而無需擔心低級細節。
圖片
幾乎你使用的每一項數字服務——社交媒體、電子商務、網上銀行、網約車應用——都依賴于幕后協同工作的各類API。
其典型工作流程如下:
- 客戶端向API發送請求。
- API部署在服務器上,它會對請求進行處理,與數據庫或其他服務進行交互,并準備響應內容。
- API以結構化格式(通常是JSON或XML)將響應發送回客戶端,客戶端能夠理解并展示這些內容。
API提供了一層抽象——客戶端無需了解服務器如何處理請求,只需知道它能返回預期的數據即可。
2.8 Rest API
在不同的 API 風格中,REST(表征狀態傳輸)是使用最廣泛的一種。
REST API 遵循一系列規則,這些規則定義了客戶端和服務器如何以結構化的方式通過 HTTP 進行通信。
圖片
REST的特點如下:
- 無狀態:每個請求都是獨立的,服務器不存儲客戶端狀態。
- 基于資源:所有事物均被視為資源(例如,/users(用戶)、/orders(訂單)、/products(產品))。
- 使用標準HTTP方法:客戶端使用以下HTTP方法與資源進行交互:
- GET → 檢索數據(例如,獲取用戶資料)。
- POST → 創建新數據(例如,添加新用戶)。
- PUT/PATCH → 更新現有數據(例如,更改用戶設置)。
- DELETE → 刪除數據(例如,刪除賬戶)。
REST API非常出色,因為它們簡單、可擴展且易于緩存,但在處理復雜數據檢索時存在局限性。
REST端點經常返回比所需更多的數據,導致網絡使用效率低下。如果API不返回相關數據,客戶端可能需要發起多個請求來檢索所有必要信息。
為了應對這些挑戰,Facebook 于 2015 年推出了 GraphQL。
2.9 GraphQL
與強制客戶端獲取固定數據集的REST不同,GraphQL允許客戶端精確請求它們所需的數據——不多也不少。
使用REST API時,如果你需要用戶詳細信息、用戶資料詳細信息以及他們的最新帖子,你可能需要向不同的端點發起多個請求:
- GET /api/users/123 → 獲取用戶詳細信息
- GET /api/users/123/profile → 獲取用戶資料
- GET /api/users/123/posts → 獲取用戶的帖子
而使用GraphQL,你可以將這些請求合并為一個,并在單個查詢中精確獲取所需的數據:
圖片
服務器只響應所請求的字段,從而減少了不必要的數據傳輸,提高了效率。
不過,GraphQL 也有其局限性--它需要在服務器端進行更多處理,而且不像 REST 那樣容易緩存。
更多GraphQL內容請查看下面鏈接:
Springboot整合GraphQL使你的API更易理解可讀性更強
2.10 數據庫
如果我們的應用程序要處理少量數據,我們可以將其存儲在內存中。
但現代應用程序處理的數據量巨大,遠遠超出了內存所能有效處理的范圍。
這就是為什么我們需要一個專門的服務器來存儲和管理數據--數據庫。
數據庫是任何應用程序的支柱。它能確保數據的存儲、檢索和高效管理,同時保證數據的安全性、一致性和持久性。
當客戶端請求存儲或檢索數據時,服務器會與數據庫通信,獲取所需的信息,并將其返回給客戶端。
圖片
但并非所有數據庫都是一樣的。不同的應用程序有不同的可擴展性、性能和一致性要求,因此選擇合適的數據庫類型非常重要。
2.11 SQL vs NoSQL
圖片
SQL數據庫以具有嚴格預定義架構的表形式存儲數據,并遵循ACID特性。
- 原子性: 事務具有全有或全無的特性(要么完全完成,要么完全不執行)
- 一致性: 數據始終保持有效并遵循定義的規則
- 隔離性: 事務之間不會相互干擾
- 持久性: 數據一旦保存,即使系統崩潰也不會丟失
由于這些保證,SQL數據庫非常適合需要強一致性和結構化關系的應用程序,例如銀行系統。
流行的SQL數據庫示例包括:MySQL和PostgreSQL
另一方面,NoSQL數據庫旨在實現高可擴展性和高性能。
它們不需要固定的架構,并使用不同的數據模型,包括:
- 鍵值存儲: 用于簡單鍵值對的快速查找(例如,Redis)
- 文檔存儲: 存儲靈活的、類似JSON的文檔(例如,MongoDB)
- 圖數據庫: 最適合高度連接的數據(例如,Neo4j)
- 寬列存儲: 針對大規模分布式數據進行了優化(例如,Cassandra)
那么,你應該使用哪一種呢?這取決于系統需求。
如果你需要具有強一致性的結構化關系數據→SQL是更好的選擇。
如果你需要高可擴展性、靈活的架構或大規模下的快速讀寫→NoSQL是更好的選擇。
許多現代應用程序同時使用SQL和NoSQL。
例如,一個電子商務平臺可能會:
- 在SQL中存儲客戶訂單(因為它們需要嚴格的一致性)
- 并在NoSQL中存儲產品推薦(因為它們需要靈活且快速的查找)
2.12 垂直擴展
隨著用戶數量的增加,應用服務器收到的請求數量也在增加。
最初,一臺服務器可能就足以處理負載。但是,隨著流量的增加,單臺服務器就會成為瓶頸,拖慢一切運行速度。
最快的解決方案之一就是升級現有服務器,增加 CPU、內存或存儲空間。
圖片
這種方法被稱為垂直擴展(縱向擴展)——即提升單臺機器的性能。
然而,這種方法存在一些重大局限性:
- 硬件限制: 你不可能無限期地升級服務器。每臺機器都有其最大容量。
- 成本: 性能更強大的服務器,其成本會呈指數級增長。
- 單點故障: 如果這臺服務器崩潰,整個系統就會癱瘓。
因此,盡管垂直擴展是一種快速解決方案,但它并非應對高流量和確保系統可靠性的長期方案。
讓我們來看看一種更好的方法——這種方法能讓我們的系統更具可擴展性和容錯能力。
2.13 水平擴展
與其升級一臺服務器,不如增加更多服務器來分擔負載,怎么樣?
圖片
這種方法被稱為水平擴展(橫向擴展)——即通過將工作負載分散到多臺機器上來實現擴展。
這種方法更為優越,原因如下:
- 服務器更多 = 容量更大: 系統能夠更有效地應對不斷增長的流量
- 無單點故障: 如果一臺服務器出現故障,其他服務器可以接管其工作,從而提高系統的可靠性
- 成本效益高: 我們無需投資于一臺價格高昂的超級服務器,而是可以使用多臺價格親民的服務器
然而,水平擴展帶來了一個新的挑戰:客戶端如何知道該連接到哪臺服務器?這時,負載均衡器就派上用場了。
2.14 負載均衡
圖片
負載均衡器位于客戶端與后端服務器之間,充當流量管理者,將請求分配到多臺服務器上。
如果某臺服務器崩潰,負載均衡器會自動將流量重定向到另一臺正常運行的服務器。
那么,負載均衡器如何決定由哪臺服務器處理下一個請求呢?
它使用負載均衡算法,例如:
- 輪詢(Round Robin): 按順序將請求依次發送給各臺服務器,循環往復
- 最少連接數(Least Connections): 將請求發送給當前活躍連接數最少的服務器。
- IP哈希(IP Hashing): 來自同一IP地址的請求始終發送到同一臺服務器,這有助于保持會話一致性。
2.15 數據庫索引
索引是加快數據庫讀取查詢速度的最快捷、最有效的方法之一。
把它想象成一本書后面的索引頁--無需翻閱每一頁,而是直接跳轉到相關部分。
數據庫索引也是如此。它是一個超級高效的查找表,可以幫助數據庫快速查找所需數據,而無需掃描整個表。
圖片
索引存儲列值以及指向表中實際數據行的指針。
索引通常創建在經常被查詢的列上,例如:
- 主鍵
- 外鍵
- 在WHERE條件中使用的列
但需謹慎——雖然索引能加快讀取速度,但會降低寫入速度(INSERT、UPDATE、DELETE),因為每當數據發生變化時,索引都需要更新。
因此,我們只應為訪問頻率最高的列創建索引。
索引能顯著提高讀取性能,但如果連索引都不足以應對,數據庫無法處理不斷增長的讀取請求,該怎么辦?
這時,我們的下一個數據庫擴展技術——復制(Replication)就派上用場了。
2.16 復制
就像我們增加更多應用服務器來處理流量一樣,我們也可以通過在多個服務器上創建數據庫副本來擴展數據庫。
圖片
工作原理:
- 我們設置一個主數據庫(也稱為“主副本”),負責處理所有寫入操作(INSERT、UPDATE、DELETE)
- 同時設置多個只讀副本,負責處理讀取查詢(SELECT)。
- 每當數據寫入主數據庫時,這些數據都會被復制到只讀副本,以確保它們保持同步。
復制技術通過將讀取請求分散到多個副本上,減輕了每個副本的負載,從而提高了讀取性能。
此外,如果主副本發生故障,某個只讀副本還可以接管成為新的主副本,這也提高了系統的可用性。
復制技術非常適合擴展讀取密集型應用,但如果我們需要擴展寫入能力或存儲海量數據,該怎么辦呢?
2.17 分片
比方說,我們的服務現在有數百萬用戶,我們的數據庫已增長到 TB 級數據量。
單個數據庫服務器最終將難以高效處理所有這些數據。
與其將所有數據集中在一個地方,我們不如將數據庫拆分成更小、更易于管理的部分,并將它們分布在多個服務器上。
圖片
這種技術被稱為分片(Sharding)
- 我們將數據庫拆分成更小的部分,稱為分片
- 每個分片包含總數據的一個子集
- 數據根據分片鍵(如用戶ID)進行分配
通過這種方式分配數據,我們可以:
- 減輕數據庫負載 → 每個分片僅處理部分查詢
- 加快讀寫性能 → 查詢被分散到多個分片上,而不是集中在一個數據庫上
分片也被稱為水平分區,因為它是按行拆分數據的。
但如果問題不在于行數過多,而在于列數過多呢?
在這種情況下,我們使用垂直分區,即按列拆分數據庫。接下來,我們將對此進行探討。
2.18 垂直分區
想象一下,我們有一個用戶表(User table),其中存儲了以下信息:
- 個人資料詳情(姓名、電子郵箱、頭像)
- 登錄歷史(最后登錄時間、IP地址)
- 以及賬單信息(賬單地址、支付詳情)
隨著該表數據量的增長,查詢速度會變慢,因為即使請求僅需要幾個特定字段,數據庫也必須掃描眾多列。
為了優化這種情況,我們可以采用垂直分區(Vertical Partitioning)方法,根據使用模式將用戶表拆分成更小、更聚焦的子表。
圖片
- User_Profile → 存儲姓名、電子郵箱、頭像
- User_Login → 存儲登錄時間戳
- User_Billing → 存儲賬單地址、支付詳情
這樣可提升查詢性能,因為每個請求只需掃描相關列,而無需掃描整個表。
這減少了不必要的磁盤I/O,使數據檢索速度更快。
然而,無論我們對數據庫進行多少優化,從磁盤檢索數據總是比從內存檢索數據慢。
如果我們能將頻繁訪問的數據存儲在內存中,以實現極快的訪問速度,那會怎樣呢?
這就是緩存(caching)的作用。
2.19 緩存
緩存通過將頻繁訪問的數據存儲在內存中,而不是從數據庫中重復獲取,從而優化系統性能。
最常見的緩存策略之一是緩存旁路模式(Cache Aside Pattern)。
圖片
工作原理:
- 當用戶請求數據時,應用程序首先檢查緩存
- 如果數據存在于緩存中,則立即返回該數據,無需訪問數據庫
- 如果數據不在緩存中,應用程序會從數據庫中檢索該數據,將其存儲在緩存中以備后續請求使用,然后將數據返回給用戶
- 下次請求相同數據時,數據將直接從緩存中提供,從而大幅加快請求速度
為防止提供過時數據,我們使用生存時間(TTL)——為緩存數據設置一個過期時間,這樣數據在經過一定時間后會自動刷新。
流行的緩存工具包括Redis和Memcached。
接下來,我們來看下一種數據庫擴展技術。
2.20 反規范化
大多數關系型數據庫采用規范化(Normalization)方法,通過將數據拆分到不同的表中來高效存儲數據。
例如,在一個電子商務系統中:
- 用戶表(Users)存儲用戶詳細信息
- 訂單表(Orders)存儲用戶的訂單
- 產品表(Products)存儲產品詳細信息
雖然這種方法減少了數據冗余,但也引入了表連接(JOIN)操作。當需要從多個表中檢索數據時,數據庫必須使用JOIN操作將它們組合起來。隨著數據集的不斷增大,這種操作可能會導致查詢速度變慢。
SELECT o.order_id, u.name, u.email, o.product, o.amount
FROM orders o
JOIN users u ON o.user_id = u.user_id;去規范化可將相關數據合并到一個表中,從而減少連接次數,即使這意味著某些數據會重復。
圖片
舉例說明: 我們不再將用戶和訂單分別保存在不同的表中,而是創建了 UserOrders 表,用于存儲用戶詳細信息及其最新訂單。
現在,在檢索用戶的訂單歷史記錄時,我們不需要進行連接操作--數據已經存儲在一起,從而提高了查詢速度和讀取性能。
SELECT order_id, user_name AS name, user_email AS email, product, amount
FROM orders;去規范化通常用于對速度要求較高的重讀取應用中,但其缺點是會增加存儲使用量和更復雜的更新請求。
2.21 CAP定理
當我們將系統擴展到多個服務器、數據庫和數據中心時,我們就進入了分布式系統的世界。
分布式系統的基本原則之一是 CAP 定理,該定理指出: 任何分布式系統都無法同時實現以下三點:
圖片
- 一致性 (C) : 每個節點總是返回最新數據
- 可用性 (A) : 即使某些節點宕機,系統也始終響應請求(但數據可能不是最新的)
- 分區容忍度 (P) : 即使節點之間出現網絡故障,系統也能繼續運行
由于網絡故障 (P) 不可避免,我們必須在以下兩者之間做出選擇:
- 一致性 + 分區容錯 (CP) : 確保每個請求都能獲得最新數據,但可能會在故障期間拒絕請求。例如:MySQL 等 SQL 數據庫: MySQL 等 SQL 數據庫
- 可用性 + 分區容錯(AP): 確保系統始終響應,即使某些數據已經過時。示例:Cassand 等 NoSQL 數據庫: Cassandra 和 DynamoDB 等 NoSQL 數據庫
在分布式 NoSQL 數據庫中,在所有服務器之間實現即時一致性的速度太慢。
相反,我們使用 "最終一致性"--這意味著:
- 并非所有節點都會立即更新,但只要有足夠的時間,它們最終會同步并返回相同的數據
- 這樣,即使在極端負載情況下,系統也能保持高可用性和快速性
最終一致性如何發揮作用:
- 用戶更新數據庫一個副本中的數據
- 系統會立即確認更新,確保高可用性
- 然后,更新會異步傳播到其他副本
- 經過短暫延遲后,所有副本都會擁有最新數據,從而確保了時間上的一致性
2.22 Blob存儲
大多數現代應用程序不僅要存儲文本記錄,還需要處理圖像、視頻、PDF 和其他大型文件。
但問題是:傳統數據庫的設計無法高效地存儲大型非結構化文件。
那么,解決方案是什么呢?
我們使用像亞馬遜 S3 這樣的 Blob 存儲,這是一種在云中存儲大型非結構化文件的高度可擴展且經濟高效的方式。
Blob 是圖像、視頻或文檔等單個文件。
這些 Blob 存儲在云中的邏輯容器或桶中。
每個文件都有一個唯一的 URL,便于在網絡上檢索和提供服務。
使用 Blob 存儲有以下幾個優勢:
- 可擴展性: 可以毫不費力地存儲 PB 級數據
- 隨用隨付定價: 只需為實際使用的存儲和檢索付費
- 自動復制: 數據在多個數據中心和可用區之間復制,以確保數據的持久性
- 輕松訪問: 可使用 REST API 或直接 URL 檢索文件
一種常見的用例是將音頻或視頻文件實時流式傳輸到用戶應用程序。
但直接從 blob 存儲器流式傳輸速度可能會很慢,尤其是當數據存儲在很遠的地方時。
2.23 CDN
例如,假設您在印度試圖觀看托管在加利福尼亞州服務器上的 YouTube 視頻。
由于視頻數據必須在世界各地傳輸,這可能會導致緩沖和加載時間變慢。
內容分發網絡(或 CDN)可以解決這個問題,它可以根據用戶的位置更快地向其分發內容。
圖片
CDN 是由分布式服務器組成的全球網絡,這些服務器協同工作,根據用戶的地理位置向其傳送網絡內容(如 HTML 頁面、JavaScript 文件、樣式表、圖像和視頻)。
CDN 將靜態內容緩存在遍布全球的多個邊緣服務器上,而不是從單個數據中心提供內容。
當用戶請求內容時,離其最近的 CDN 服務器就會提供內容,而不是一直傳送到原始服務器。
由于內容是從最近的 CDN 節點提供的,因此用戶可以體驗到更快的加載時間和最小的緩沖。
2.24 WebSockets
大多數網絡應用程序都使用 HTTP,它遵循請求-響應模式。
- 客戶端發送請求
- 服務器處理請求并發送響應
- 如果客戶端需要新數據,則必須發送另一個請求
這種方式對于靜態網頁很有效,但對于實時應用程序(如即時聊天應用程序、股票市場儀表板和多人在線游戲)來說,速度太慢,效率太低。
使用 HTTP,獲得實時更新的唯一方法是輪詢--每隔幾秒發送一次重復請求。
但輪詢的效率很低,因為它會增加服務器負載并浪費帶寬,因為大多數響應都是空的(當沒有新數據時)。
WebSockets 解決了這個問題,它允許客戶端和服務器之間通過單個持久連接進行連續的雙向通信。
圖片
工作原理:
- 客戶端啟動與服務器的 WebSocket 連接
- 連接一旦建立,就會保持打開狀態
- 服務器可隨時向客戶端推送更新,無需等待請求
- 客戶端也可以即時向服務器發送信息
2.25 Webhooks
Webhooks 允許服務器在事件發生時立即向另一臺服務器發送 HTTP 請求,而不是不斷輪詢 API 來檢查事件是否發生。
圖片
工作原理:
- 接收方(你的應用程序)向提供商(如 Stripe、GitHub、Twilio)注冊一個 webhook URL
- 事件發生時(如用戶付款),提供商會向網絡鉤子 URL 發送 HTTP POST 請求,并提供事件詳情
- 你的應用程序會處理傳入的請求并相應地更新數據
2.26 微服務
傳統上,應用程序都是采用單片式架構構建的,即:
- 所有功能(如身份驗證、支付、訂單、發貨)都在一個大型代碼庫中
- 如果系統的某個部分出現故障或需要擴展,整個系統都會受到影響
- 部署是有風險的,一個錯誤的更新就可能導致整個應用程序癱瘓
舉例說明: 想象一下,在一個電子商務應用程序中,訂單、付款、庫存和發貨模塊都在一個代碼庫中緊密相連。
如果庫存系統崩潰,整個應用程序就會癱瘓。
單體系統對于小型應用程序來說很好用,但對于大型系統來說,就很難管理、擴展和部署了。
解決方案是將應用程序分解成更小的、獨立的服務,這些服務被稱為微服務,它們可以協同工作。
圖片
每個微服務:
- 處理單一職責
- 擁有自己的數據庫和邏輯,因此可以獨立擴展
- 使用應用程序接口或消息隊列與其他微服務通信
這樣,服務就可以單獨擴展和部署,而不會影響整個系統。
但是,當多個微服務需要通信時,直接調用 API 并不總是高效的,這就是消息隊列的用武之地。
2.27 消息隊列
在單體系統中,函數會直接互相調用并等待響應。
但在基于微服務的系統中,這種方法效率很低,因為:
- 如果一項服務運行緩慢或出現故障,所有服務都將等待
- 高流量會使單項服務超負荷
- 同步通信(等待立即響應)無法很好地擴展
消息隊列能讓服務進行異步通信,從而在不阻塞其他操作的情況下處理請求。
圖片
工作原理:
- 生產者(如結賬服務)將信息放入隊列(如 "處理付款")
- 隊列暫時保存消息,直到消費者(如支付服務)準備好處理它
- 消費者檢索信息并進行處理
利用消息隊列,我們可以解耦服務,提高可擴展性和容錯性。
常見的消息隊列系統包括 Apache Kafka、RocketMQ 和 RabbitMQ。
2.28 速率限制(限流)
想象一下,一個機器人開始每秒向您的網站發出數千次請求。
如果不加限制,這可能:
- 消耗所有可用資源,導致服務器崩潰
- 由于過度使用 API 而增加云成本
- 降低合法用戶的性能
速率限制可限制客戶端在特定時間內發送請求的數量。
圖片
工作原理:
- 每個用戶或 IP 地址都有一個請求配額(例如每分鐘 100 個請求)
- 如果超過這個限額,服務器會暫時阻止其他請求,并返回錯誤信息(HTTP 429 - 請求過多)
有各種速率限制算法,常用的有:
- 固定窗口: 根據固定的時間窗口(如每分鐘 100 個請求)限制請求
- 滑動窗口: 更靈活的版本,可動態調整限制,以平滑處理突發請求
- 令牌桶: 用戶的請求會獲得令牌,令牌會以固定比率隨時間補充
2.29 API網關
API 網關是一種集中式服務,用于處理身份驗證、速率限制、日志記錄和監控以及請求路由。
想象一下,一個基于微服務的應用程序包含多個服務。
API 網關不是直接公開每個服務,而是充當所有客戶端請求的單一入口。
圖片
工作原理:
- 客戶端向 API 網關發送請求
- 網關驗證請求(如身份驗證、速率限制)
- 它將請求路由到相應的微服務
- 響應通過網關發回客戶端
API 網關簡化了 API 管理,提高了可擴展性和安全性。
2.30 冪等性
在分布式系統中,網絡故障和服務重試很常見。如果用戶不小心刷新了付款頁面,系統可能會收到兩個付款請求,而不是一個。
圖片
冪等性確保重復請求產生的結果與只請求一次的結果相同。
工作原理:
- 每個請求都有一個唯一的 ID(如 request_1234)
- 在處理之前,系統會檢查該請求是否已被處理過
- 如果是: 則忽略重復請求
- 如果否: 正常處理該請求
冪等性可以防止重復事務,確保分布式系統的數據一致性。


























