系統設計系列文30:從一台 Server 到今天:PokeThreads 系統設計回顧
30 天前,這個系列是從一句話開始的:
System Design 的第一步,不是畫架構圖,而是先把問題問清楚。
今天是最後一天,與其再加一個新元件,不如看看我們怎麼從 Day 1 那句話,走到今天這個樣子的。
從需求拆解到架構設計、擴展與維運
檢視所有標籤30 天前,這個系列是從一句話開始的:
System Design 的第一步,不是畫架構圖,而是先把問題問清楚。
今天是最後一天,與其再加一個新元件,不如看看我們怎麼從 Day 1 那句話,走到今天這個樣子的。
昨天盤點了 PokeThreads 的攻擊面,列出幾個明確的缺口:擋不住分散式的爬蟲與洗版、看不懂檔案內容、管不到內部服務之間的信任、也完全沒有「這個人能不能做這件事」的概念。
今天把這些缺口一個一個補上。

前面我們都在處理使用者變多的問題。今天討論資安層面,換一個角度提問:
「如果有人存心想搞垮這個系統,或是想從裡面偷走一些不該拿到的東西,他會從哪裡下手?」
這種系統性盤點系統弱點的方法,叫做 Threat Modeling(威脅模型),核心做法很直接:把系統的每一個對外(甚至對內)的入口都列出來,一個一個討論「這裡可能出什麼問題,誰會想讓它出問題」。這些入口的集合,就是這個系統的 Attack Surface(攻擊面)。
值得先說清楚:今天不是要重新設計任何元件,而是用一雙攻擊者的眼睛,重新檢視我們這 27 天蓋出來的東西。

到目前為止,我們的 PokeThreads 已是一套有相當程度的分散式系統,元件包含 Load Balancer、Stateless Server、Read Replica、Sharding、Cache、CDN、Message Queue、Search、Counter System,但這些元件都隱含了一個沒有明講的假設,就是它們全部都放在同一個資料中心(Data Center)裡。
Day 9 我們用 CDN 解決了圖片、影片的全球傳遞問題,不管使用者在哪裡,靜態檔案都能就近從 Edge 拿到。但今天要問一個更根本的問題:
如果 Pikachu 人在亞洲,我們的 Server、Database 卻全部蓋在美國,會發生什麼事?
前面內容幾乎都在講後端,今天把視角切回前端。
走過這麼多次的設計與迭代,PokeThreads 已經不是一個「單純」的系統了。
一個 GET /feed Request,背後可能經過:
API Gateway -> Rate Limiter -> Stateless Server -> Cache
-> Sharded Database(也可能是 Search Index)-> Message Queue 的下游 Worker
當所有元件都正常運作時,一切都很美好。
但如果某天使用者 Pikachu 跳出來抱怨:「PokeThreads 的 Feed 怎麼變超慢?」
我們要怎麼知道問題出在哪裡:
在元件這麼多的系統裡,光憑猜測完全沒有效率,這正是 Observability(可觀測性) 要解決的問題。
Day 11 我們做出了最陽春的 Notification,Day 12 用 Message Queue 把它從同步流程裡拆了出去:
這解決了「同步處理拖累主流程」的問題,但留下一個新的使用者體驗問題:
Notification Worker 把通知寫進 Database 之後呢?
Pikachu 現在要收到這則通知,還是得自己重整頁面或是重打 App,讓前端再呼叫一次 GET /notifications 才會看到。
對一個社群平台來說,這種「使用者要自己去問,系統才回答」的方式,體驗上明顯不夠即時。
PokeThreads 裡有一種資料,我們從很早就開始提到,卻從來沒有認真設計過,就是計數(Counter)。
Day 8 討論 Cache 的時候,用 Like Count 當例子說明什麼資料適合被 Cache;Day 17 討論 CAP Theorem 時,又拿 Like Count 當作 Eventual Consistency 的範例。
今天終於要正面處理這個問題:Like、View、Follower 這些數字,到底該怎麼設計才撐得住?
我們的 PokeThreads 走到現在,已經是一個具備 Load Balancer、Stateless Server、Sharded Database、Cache、CDN、Message Queue 的系統了,但有一個使用者天天在用、我們卻從來沒認真設計過的功能:搜尋。
Pikachu 想找某篇提到「寶可夢」的貼文,或是想直接搜尋 Charmander 這個帳號,這種需求聽起來理所當然,但要做好一點都不簡單。
昨天在討論 Fan-out 時提到,擁有幾千萬追蹤者的帳號一發文,就可能讓 Fan-out on Write 的寫入量瞬間爆炸,所以今天就把這個狀況抽出來單獨討論。