From 846c44c35ed6b1e6e210b5f24d895cae7dd51bad Mon Sep 17 00:00:00 2001 From: Lu Shueh Chou Date: Mon, 2 Sep 2024 10:11:49 +0800 Subject: [PATCH] feat(nalsd): add intro of cross dc --- src/essay/web/tcp.md | 39 +++++++++--- .../distributed-ft.md | 2 +- .../site-reliability-workbook/nalsd.md | 62 ++++++++++++++----- src/stylesheets/custom.css | 5 ++ 4 files changed, 83 insertions(+), 25 deletions(-) diff --git a/src/essay/web/tcp.md b/src/essay/web/tcp.md index 61f76452..70436f9a 100644 --- a/src/essay/web/tcp.md +++ b/src/essay/web/tcp.md @@ -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) @@ -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)代表的意義在下段展示。 diff --git a/src/feedback/designing-data-intensive-applications/distributed-ft.md b/src/feedback/designing-data-intensive-applications/distributed-ft.md index 3a4895ce..7a1be272 100644 --- a/src/feedback/designing-data-intensive-applications/distributed-ft.md +++ b/src/feedback/designing-data-intensive-applications/distributed-ft.md @@ -239,7 +239,7 @@ _我正要執行「計數為六」的異動,請你的計數加一_。 !!! info 線性系統比全域順序廣播[更易](http://courses.csail.mit.edu/6.852/08/papers/CT96-JACM.pdf)實作出來。 -**怎麼達成?** +##### 怎麼達成? ![醫生值班例子又回來了](https://i.imgur.com/HwJP8wO.png) diff --git a/src/feedback/site-reliability-workbook/nalsd.md b/src/feedback/site-reliability-workbook/nalsd.md index c1fbcf1e..82d46e8d 100644 --- a/src/feedback/site-reliability-workbook/nalsd.md +++ b/src/feedback/site-reliability-workbook/nalsd.md @@ -20,10 +20,10 @@ tags: SRE-workbook 思考系統的擴充性和可能的瓶頸,並專注在這些點上。 在接下來的練習中,每一次設計的迭代,都可以反覆問自己這四個問題: -- **這個設計可能嗎?**假設不去管資源議題,這個設計可以滿足需求嗎? -- **有沒有更好的方法?**可以讓他更快、更輕便、更有效率嗎? -- **這方法可以在有限的設備數量、時間和金錢內達成嗎?** -- **這個架構允許降能嗎?**當某組建壞掉會發生什麼事?當資料中心壞掉會發生什麼事? +- *這個設計可能嗎?* 假設不去管資源議題,這個設計可以滿足需求嗎? +- *有沒有更好的方法?* 可以讓他更快、更輕便、更有效率嗎? +- *這方法可以在有限的設備數量、時間和金錢內達成嗎?* +- *這個架構允許降能嗎?* 當某組建壞掉會發生什麼事?當資料中心壞掉會發生什麼事? !!! success "練習的目的" 所有的系統最終都要實際跑在真實的資料中心和真實的設備上, @@ -212,11 +212,11 @@ WHERE a.search_term = b.search_term title: LogJoiner 架構 --- flowchart TD - ql[Query Logs] --All query
log records-->qm[(QueryMap
key: ad_id,
search_term
value: query_ids)] - ql --All query
log records-->qs[(QueryStore
key: query_id
value: Query
Log record)] - cl[Click Logs]--All click log
records-->lj([LogJoiner]) - lj<--Look up
query_id-->qs - lj --> cm[(CLickMap
key: ad_id,
search_term
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` 對應的點擊數和搜尋數。 @@ -268,6 +268,8 @@ QueryStore 只需要以 `query_id` 作為鍵,然後存放該筆搜尋的資料 = 2 \mathrm{\ TB/day} \end{align*} +問題:*有沒有更好的方法?* + 雖然可以用一台機器的磁碟來存放 2TB 的資料,但是前面我們也看到他容易受到 IOPS 的限制, 所以我們勢必需要設計一個可以進行[分區]的資料叢集,接著我們就往下討論此架構。 @@ -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 節點, +以避免有特定節點失能時,該區段的資料都會丟失,導致輕易超過我們的錯誤預算。 最終的架構就會是: @@ -294,8 +301,8 @@ QueryStore 只需要以 `query_id` 作為鍵,然後存放該筆搜尋的資料 title: 分區的 LogJoiner 架構 --- flowchart TD - ql[Query Logs] --> qls[Query Log Sharder
hash:ad_id % N] --> qm1 & qm2 - cl[Click Logs] --> cls[Click Log Sharder
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)] @@ -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 是一個設計系統時反覆迭代的過程,首先把架構拆成對應的邏輯元件,並想像其置入線上環境的資源需求。 diff --git a/src/stylesheets/custom.css b/src/stylesheets/custom.css index ec904b11..aea947d5 100644 --- a/src/stylesheets/custom.css +++ b/src/stylesheets/custom.css @@ -10,3 +10,8 @@ --md-typeset-color: #fafafa; } } + +/* mermaid styling */ +.mermaid .flowchartTitleText { + fill: var(--md-typeset-color) !important; +}