iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0

上一篇估算出來的流量其實非常小,每秒寫入大概 1~2 次、讀取大概 100 多次,這種規模不需要任何花俏的架構,先做出一個 「堪用」 的版本就好。

這也是這個系列一路會遵守的原則:

不要一開始就把系統蓋大,而是先用最簡單的方式解決問題,避免過度工程(Over-engineering)。

目的:先讓系統能動起來

在加任何元件之前,第一版的目標只有一個:

讓「發文、看 Feed、追蹤」這三件事情能夠正常運作。

延續昨天定出的範疇,這一版先不處理:

  • 圖片、影片
  • 通知
  • 搜尋、推薦
  • 大流量、多台機器、分散式問題

先把這些全部砍掉,只留下最核心的骨架。

最簡單可行實作

架構

在只有一台 Server 就能撐住流量的前提下,第一版架構單純到不能再單純,簡直像古早時代的大學留言板:

瀏覽器 -> Server -> Database
   ↑↓
  DNS

使用者在瀏覽器輸入網址,DNS 把網域名稱換成 IP,瀏覽器再把 Request 送到我們唯一的一台 Server,Server 讀寫 Database 之後把結果回傳。

PokeThreads 第一版:瀏覽器、Server、Database 的簡易架構圖

沒有 Load Balancer 也沒有 Cache,如果你不清楚這些元件也沒關係,因為現在完全不需要,這些元件要解決的問題,我們根本還沒遇到,之後會再介紹。

Database Schema

資料表也是先求有再求好,這裡先把每個欄位的資料型別也一併定義出來:

  • User
    • id:BIGINT(Primary Key)
    • email:VARCHAR(255)
    • password_hash:VARCHAR(255)
    • nickname:VARCHAR(50)
  • Post
    • id:BIGINT(Primary Key)
    • author_id:BIGINT(Foreign Key → User.id)
    • content:TEXT
    • created_at:TIMESTAMP
  • Follow
    • id:BIGINT(Primary Key)
    • follower:BIGINT(Foreign Key → User.id)
    • followee:BIGINT(Foreign Key → User.id)

follower 是發起追蹤的人,followee 是被追蹤的人。如果 Pikachu 追蹤 Charmander,就會多一筆:

follower = Pikachu
followee = Charmander

API 設計

先定義三支最核心的 API。

追蹤使用者

POST /follow
{
  "user_id": 123
}

發布貼文

POST /posts
{
  "content": "Hello PokeThreads"
}

取得 Feed

GET /feed

這裡故意先不展開 GET /feed 該回傳哪些貼文,明天會針對 Feed 單獨討論,今天只需要知道「這支 API 存在」就夠了。

登入後 Server 怎麼記得是誰?

發文、看 Feed 都建立在一個前提上:Server 要知道現在是誰在發 Request。

目前只有一台 Server,最直覺的做法就是把登入狀態(Session)直接放進這台 Server 的 Memory:

Server
├── Application
└── Session(存在 Memory 裡)
     ├── Pikachu
     ├── Charmander
     └── Squirtle

流程是這樣的:

  1. 使用者輸入帳號密碼登入
  2. Server 驗證成功後,在自己的 Memory 建立一筆 Session,並發一個 Session ID 給瀏覽器(通常放在 Cookie)
  3. 之後每個 Request,瀏覽器都帶著這個 Session ID
  4. Server 從自己的 Memory 查到對應的使用者身份

這種「Server 自己保存使用者狀態」的設計,稱為 Stateful Server

它的好處就是非常簡單,缺點也很明顯:一旦以後多開一台 Server,新 Server 的 Memory 裡完全沒有這筆 Session,不過這也是之後才會遇到的問題,先知道有這問題就好。

業界做法

即使是最陽春的登入機制,業界常見的做法大致分兩派:

  • Session-based:像上面這樣,狀態存在 Server 端,瀏覽器只帶一個代號(Session ID)。
  • Token-based(如 JWT):狀態直接編碼進一個簽章過的 Token,Server 不需要另外保存任何東西,靠驗證簽章就能確認身份。

在單台 Server、使用者不多的情況下,兩種做法差異不大。

Session-based 的優勢是「要收回權限」很直接(例如踢掉某個使用者,只要刪掉 Server 上的那筆 Session 就好)。

Token-based 則是天生比較適合之後多台 Server 的場景,因為身份驗證不依賴任何一台特定 Server 的記憶體。

這系列先選 Session-based 起步,之後多台 Server 出現時再認真討論。

前端:先分兩塊就好

前端也不用想太多,先切成兩個區塊:

  • Feed:負責打 API 拿貼文、把貼文渲染出來
  • Post Composer:一個輸入框加一個送出按鈕,負責打 POST /posts

發文流程

使用者輸入文字 -> 按下發布
        ↓
  POST /posts -> Server -> Database

看 Feed 流程

使用者打開頁面
        ↓
   GET /feed -> Server -> Database(取出 Posts)
        ↑                      ↓
        └──────── 回傳貼文 ──────┘

會員以及追蹤功能也非常簡單,不贅述了。

與其他元件串接

現在系統裡只有三個角色:瀏覽器、Server、Database,彼此的關係非常直白,Server 是唯一的窗口,所有讀寫都要經過它。

之後不管加 Cache、加 CDN,還是拆多台 Server,這個「Server 對外接 Client,對內接 Database」的基本分工都不會變,改變的只是這兩層各自要不要再增加元件。

小結

到這裡,第一版最陽春的 PokeThreads 已經完成:

  • 一台 Server、一個 Database
  • 可以發文、看 Feed、追蹤別人
  • 登入狀態先用最簡單的 Stateful Server 處理

它撐不住太大的流量,也還沒有任何容錯機制,但它已經是一個能正常運作、而且我們完全理解它為什麼能動的系統。

不過剛剛在設計 GET /feed 的時候,其實刻意跳過了一個問題:

Feed 到底應該回傳哪些貼文?又該用什麼順序排?

這個問題,就留到明天處理。


上一篇
Day 1 什麼是軟體系統設計?
下一篇
Day 3 Feed 怎麼產生?
系列文
系統設計就像九頭蛇:打造社群網站的 30 天10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言