Skip to content

Commit

Permalink
feat(nalsd): add intro of cross dc
Browse files Browse the repository at this point in the history
  • Loading branch information
evan361425 committed Sep 2, 2024
1 parent 01a263c commit 846c44c
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 25 deletions.
39 changes: 30 additions & 9 deletions src/essay/web/tcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,18 @@ Network 中的 IP 是一種不考慮連線的協定,他只需要負責把封
換句話說,TCP 是被設計成雙向(bidirectional)、序列性(ordered)和可靠(reliable)的資料傳輸協定。

- 雙向:開啟連線時,這個連線雙方都可以寫入和讀取的;
- 序列:透過序列(Sequence 或簡稱 SEQ) 和確認(Acknowledgement 或簡稱 ACK)的編號確認;
- 序列:透過序列(Sequence 或簡稱 SEQ和確認(Acknowledgement 或簡稱 ACK)的編號確認;
- 可靠:透過反覆寄送、檢查信號 ACK,確認雙方認知一致。

!!! info "阜口"
IP 協定透過 IPv4 或 IPv6 找到正確的設備,
而 TCP 透過阜口(port)在同一台設備中分辨不同的連線。

換句話說,一台設備可能會同時有多個阜口正在接收發送資訊。
而請求方的阜口的範圍稱其為[暫時阜口](https://en.wikipedia.org/wiki/Ephemeral_port)(ephemeral port)
這範圍通常是可以設定的,同時預設也會根據 kernel 的不同而有差異。
反之,服務方則通常是允許應用程式自由選擇。

## 內容物

![TCP 標頭的可能內容物](https://i.imgur.com/3nh6DOI.png)
Expand All @@ -35,18 +44,30 @@ TCP 會透過上述各種編號和標記來完成連線所需的溝通。

建立連線:

- 監聽方透過 [socket API](#bsd-socket-api) CONNECT 來等待連線;
- 請求方 *要求建立*
- 監聽同樣 *要求建立* 且透過 ACK 同意原請求;
- 請求方 *同意建立請求* 自此,連線完成建立。
- 監聽方(server)透過 [socket API](#bsd-socket-api) LISTEN 來等待連線;
- 請求方(client)透過 `SYN` *要求建立*
- 監聽方同樣透過 `SYN` *要求建立* 且透過 `ACK` 同意原請求;
- 請求方透過 `ACK` *同意建立請求* 自此,連線完成建立。
此時,請求方可能會同時把資料一起送出。

```mermaid
sequenceDiagram
participant c as Client
participant s as Server
s-->s: socket API: LISTEN
c->>+s: SYN, 要求建立
s->>-c: ACK/SYN, 同意且要求建立
c->>+s: ACK, 同意可能同時挾帶資料
Note right of c: 同時挾帶,就只需要一次 RTT
```

關閉連線:

- 任意一方 *要求關閉*,進入主動關閉方;
- 收到的人,照常回應 ACK 同步認知,進入關閉等待期。
如確認資料都送出,且同意關閉後會同樣送出 *關閉要求* 並結束該連線;
- 此時,主動關閉方根據收到的標記,進入不同狀態,最終走入暫置區(`TIME_WAIT`)。
- 任意一方透過 `FIN` *要求關閉*,進入主動關閉方;
- 收到的人成為被動方,回應 `ACK` 同步認知,進入關閉等待期。
被動方如確認資料都送出,且同意關閉後會透過 `FIN` 提出*關閉要求* 並結束該連線;
- 此時,主動關閉方根據收到的標記,進入不同狀態,最終走入暫置區 `TIME_WAIT`
- 當盡可能確認所有封包都送出或收到,主動方就釋放阜口並允許其再次被使用。

各個標記(flag)代表的意義在下段展示。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ _我正要執行「計數為六」的異動,請你的計數加一_。
!!! info
線性系統比全域順序廣播[更易](http://courses.csail.mit.edu/6.852/08/papers/CT96-JACM.pdf)實作出來。

**怎麼達成?**
##### 怎麼達成?

![醫生值班例子又回來了](https://i.imgur.com/HwJP8wO.png)

Expand Down
62 changes: 47 additions & 15 deletions src/feedback/site-reliability-workbook/nalsd.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ tags: SRE-workbook
思考系統的擴充性和可能的瓶頸,並專注在這些點上。
在接下來的練習中,每一次設計的迭代,都可以反覆問自己這四個問題:

- **這個設計可能嗎?**假設不去管資源議題,這個設計可以滿足需求嗎?
- **有沒有更好的方法?**可以讓他更快、更輕便、更有效率嗎?
- **這方法可以在有限的設備數量、時間和金錢內達成嗎?**
- **這個架構允許降能嗎?**當某組建壞掉會發生什麼事?當資料中心壞掉會發生什麼事?
- *這個設計可能嗎?* 假設不去管資源議題,這個設計可以滿足需求嗎?
- *有沒有更好的方法?* 可以讓他更快、更輕便、更有效率嗎?
- *這方法可以在有限的設備數量、時間和金錢內達成嗎?*
- *這個架構允許降能嗎?* 當某組建壞掉會發生什麼事?當資料中心壞掉會發生什麼事?

!!! success "練習的目的"
所有的系統最終都要實際跑在真實的資料中心和真實的設備上,
Expand Down Expand Up @@ -212,11 +212,11 @@ WHERE a.search_term = b.search_term
title: LogJoiner 架構
---
flowchart TD
ql[Query Logs] --All query<br>log records-->qm[(QueryMap<br>key: ad_id,<br>search_term<br>value: query_ids)]
ql --All query<br>log records-->qs[(QueryStore<br>key: query_id<br>value: Query<br>Log record)]
cl[Click Logs]--All click log<br>records-->lj([LogJoiner])
lj<--Look up<br>query_id-->qs
lj --> cm[(CLickMap<br>key: ad_id,<br>search_term<br>value: query_ids)]
ql[Query Logs] --All query\nlog records-->qm[(QueryMap\nkey: ad_id,\nsearch_term\nvalue: query_ids)]
ql --All query\nlog records-->qs[(QueryStore\nkey: query_id\nvalue: Query\nLog record)]
cl[Click Logs]--All click log\nrecords-->lj([LogJoiner])
lj<--Look up\nquery_id-->qs
lj --> cm[(CLickMap\nkey: ad_id,\nsearch_term\nvalue: query_ids)]
```

透過 ClickMap 和 QueryMap 存放我們需要的 `ad_id``search_term` 對應的點擊數和搜尋數。
Expand Down Expand Up @@ -268,6 +268,8 @@ QueryStore 只需要以 `query_id` 作為鍵,然後存放該筆搜尋的資料
= 2 \mathrm{\ TB/day}
\end{align*}

問題:*有沒有更好的方法?*

雖然可以用一台機器的磁碟來存放 2TB 的資料,但是前面我們也看到他容易受到 IOPS 的限制,
所以我們勢必需要設計一個可以進行[分區]的資料叢集,接著我們就往下討論此架構。

Expand All @@ -279,13 +281,18 @@ QueryStore 只需要以 `query_id` 作為鍵,然後存放該筆搜尋的資料
- 高可用:不只分區,也可以把資料複製到多台節點,以達到高可用性;
- 效率性:當資料夠小時,就可以使用記憶體來儲存,加快整個流程。

問題:*這個設計可能嗎?*

相關做法就是對 `ad_id` 做雜湊得到一個數字後,根據該數字的位置分配給指定的節點,
例如假設有 N 個節點,把流量分配給雜湊結果模除(modulo)N 得到的數字。
當廣告商想要找特定廣告的點擊率時,就用一樣的方法去做搜尋。

同樣的做法也可以套用在 LogJoiner,只是會根據 `query_id` 進行分配。
最後,為了達到高可用,我們可以把請求同時分配給兩個不同的 LogJoiner、QueryMap 或 ClickMap,
以避免有節點突然失能,超過我們的錯誤預算。

問題:*這個架構允許降能嗎?*

最後,為了達到高可用,我們可以使用[複製](../designing-data-intensive-applications/distributed-replication.md)
把請求同時分配給多個不同的 LogJoiner、QueryMap 或 ClickMap 節點,
以避免有特定節點失能時,該區段的資料都會丟失,導致輕易超過我們的錯誤預算。

最終的架構就會是:

Expand All @@ -294,8 +301,8 @@ QueryStore 只需要以 `query_id` 作為鍵,然後存放該筆搜尋的資料
title: 分區的 LogJoiner 架構
---
flowchart TD
ql[Query Logs] --> qls[Query Log Sharder<br>hash:ad_id % N] --> qm1 & qm2
cl[Click Logs] --> cls[Click Log Sharder<br>hash:query_id % M] --> lj1 & lj2
ql[Query Logs] --> qls[Query Log Sharder\nhash:ad_id % N] --> qm2 & qm1
cl[Click Logs] --> cls[Click Log Sharder\nhash:query_id % M] --> lj2 & lj1
subgraph qms [QueryMap Shards]
qm1[(QueryMap Shard 1)]
qm2[(QueryMap Shard N)]
Expand All @@ -308,13 +315,38 @@ flowchart TD
cm1[(ClickMap Shard 1)]
cm2[(ClickMap Shard K)]
end
ljs --> cm1 & cm2
ljs --> cm2 & cm1
ql --> qs[(QueryStore)]
qs <--> ljs
```

??? question "為什麼要用 query_id 做分區的鍵"
可以看到不同的日誌使用不同的鍵,點擊日誌使用 `query_id` 分區,而搜尋日誌使用 `ad_id`
為什麼這樣選擇?

對於 QueryMap 來說,它會使用 `ad_id` 作為鍵,所以搜尋日誌透過 `ad_id` 來分區很合理。
但是對點擊日誌來說,它會需要使用 `query_id` 來和 QueryStore 拿取搜尋資訊,
透過 `query_id` 來分區可以和它後續動作匹配,最後在根據 `ad_id` 分發給不同的 ClickMap。
當然,實作時可以根據相關數據來進行分析,怎麼樣效率更好,或更直觀。

### 延伸架構去滿足 SLO

如果我們要盡可能讓他高可用,單一叢集的多節點之外,我們也要做到多叢集。
換句話說,什麼樣的架構可以滿足多資料中心的高可用呢?

問題:*這個架構允許更高強度的降能嗎?*

為了避免單一叢集的風險,我們需要在收到點擊或搜尋日誌時,同時送給其他叢集。
但是在分發的同時,要怎麼避免彼此狀態不同步,導致使用者得到的 CTR 會不一樣?
這個問題是經典的分散式容錯。

!!! info "分散式容錯"
這裡不展開什麼是分散式容錯,或者說共識演算法,因為這個東西如果用兩三行來解釋,很容易造成誤會。
不過之前有寫過一些[相關介紹](../designing-data-intensive-applications/distributed-ft.md),歡迎閱讀。

根據評估,每次資料的同步需要約 25ms,這個時間會因為兩個資料中心的距離遠近而有所改變,
換句話說,單一連線每秒最多只能進行 25 次的同步。

## 總結

NALSD 是一個設計系統時反覆迭代的過程,首先把架構拆成對應的邏輯元件,並想像其置入線上環境的資源需求。
Expand Down
5 changes: 5 additions & 0 deletions src/stylesheets/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@
--md-typeset-color: #fafafa;
}
}

/* mermaid styling */
.mermaid .flowchartTitleText {
fill: var(--md-typeset-color) !important;
}

0 comments on commit 846c44c

Please sign in to comment.