13 | 架構設計流程:詳細方案設計

此爲筆記

課程鏈接

https://time.geekbang.org/column/intro/100006601?utm_source=time_web&utm_medium=menu&utm_term=timewebmenu


完成備選方案的設計和選擇後,我們終於可以長出一口氣,因爲整個架構設計最難的一步已經完成了,但整體方案尚未完成,架構師還需繼續努力。接下來我們需要再接再勵,將最終確定的備選方案進行細化,使得備選方案變成一個可以落地的設計方案。所以今天我來講講架構設計流程第 4 步:詳細方案設計。

架構設計第 4 步:詳細方案設計

簡單來說,詳細方案設計就是將方案涉及的關鍵技術細節給確定下來。

  • 假如我們確定使用 Elasticsearch 來做全文搜索,那麼就需要確定 Elasticsearch 的索引是按照業務劃分,還是一個大索引就可以了;副本數量是 2 個、3 個還是 4 個,集羣節點數量是 3 個還是 6 個等。

  • 假如我們確定使用 MySQL 分庫分表,那麼就需要確定哪些表要分庫分表,按照什麼維度來分庫分表,分庫分表後聯合查詢怎麼處理等。

  • 假如我們確定引入 Nginx 來做負載均衡,那麼 Nginx 的主備怎麼做,Nginx 的負載均衡策略用哪個(權重分配?輪詢?ip_hash?)等。

可以看到,詳細設計方案裏面其實也有一些技術點和備選方案類似。例如,Nginx 的負載均衡策略,備選有輪詢、權重分配、ip_hash、fair、url_hash 五個,具體選哪個呢?看起來和備選方案階段面臨的問題類似,但實際上這裏的技術方案選擇是很輕量級的,我們無須像備選方案階段那樣操作,而只需要簡單根據這些技術的適用場景選擇就可以了。

例如,Nginx 的負載均衡策略,簡單按照下面的規則選擇就可以了。

  • 輪詢(默認)

每個請求按時間順序逐一分配到不同的後端服務器,後端服務器分配的請求數基本一致,如果後端服務器“down 掉”,能自動剔除。

  • 加權輪詢

根據權重來進行輪詢,權重高的服務器分配的請求更多,主要適應於後端服務器性能不均的情況,如新老服務器混用。

  • ip_hash

每個請求按訪問 IP 的 hash 結果分配,這樣每個訪客固定訪問一個後端服務器,主要用於解決 session 的問題,如購物車類的應用。

  • fair

按後端服務器的響應時間來分配請求,響應時間短的優先分配,能夠最大化地平衡各後端服務器的壓力,可以適用於後端服務器性能不均衡的情況,也可以防止某臺後端服務器性能不足的情況下還繼續接收同樣多的請求從而造成雪崩效應。

  • url_hash

按訪問 URL 的 hash 結果來分配請求,每個 URL 定向到同一個後端服務器,適用於後端服務器能夠將 URL 的響應結果緩存的情況。

這幾個策略的適用場景區別還是比較明顯的,根據我們的業務需要,挑選一個合適的即可。例如,比如一個電商架構,由於和 session 比較強相關,因此如果用 Nginx 來做集羣負載均衡,那麼選擇 ip_hash 策略是比較合適的。

詳細設計方案階段可能遇到的一種極端情況就是在詳細設計階段發現備選方案不可行,一般情況下主要的原因是備選方案設計時遺漏了某個關鍵技術點或者關鍵的質量屬性。例如,我曾經參與過一個項目,在備選方案階段確定是可行的,但在詳細方案設計階段,發現由於細節點太多,方案非常龐大,整個項目可能要開發長達 1 年時間,最後只得廢棄原來的備選方案,重新調整項目目標、計劃和方案。這個項目的主要失誤就是在備選方案評估時忽略了開發週期這個質量屬性。

幸運的是,這種情況可以通過下面方式有效地避免:

  • 架構師不但要進行備選方案設計和選型,還需要對備選方案的關鍵細節有較深入的理解。例如,架構師選擇了 Elasticsearch 作爲全文搜索解決方案,前提必須是架構師自己對 Elasticsearch 的設計原理有深入的理解,比如索引、副本、集羣等技術點;而不能道聽途說 Elasticsearch 很牛,所以選擇它,更不能成爲把“細節我們不討論”這句話掛在嘴邊的“PPT 架構師”。

  • 通過分步驟、分階段、分系統等方式,儘量降低方案複雜度,方案本身的複雜度越高,某個細節推翻整個方案的可能性就越高,適當降低複雜性,可以減少這種風險。

  • 如果方案本身就很複雜,那就採取設計團隊的方式來進行設計,博採衆長,彙集大家的智慧和經驗,防止只有 1~2 個架構師可能出現的思維盲點或者經驗盲區。

詳細方案設計實戰

雖然我們上期在“前浪微博”消息隊列的架構設計挑選了備選方案 2 作爲最終方案,但備選方案設計階段的方案粒度還比較粗,無法真正指導開發人員進行後續的設計和開發,因此需要在備選方案的基礎上進一步細化。

下面我列出一些備選方案 2 典型的需要細化的點供參考,有興趣的同學可以自己嘗試細化更多的設計點。

1. 細化設計點 1:數據庫表如何設計?

  • 數據庫設計兩類表,一類是日誌表,用於消息寫入時快速存儲到 MySQL 中;另一類是消息表,每個消息隊列一張表。

  • 業務系統發佈消息時,首先寫入到日誌表,日誌表寫入成功就代表消息寫入成功;後臺線程再從日誌表中讀取消息寫入記錄,將消息內容寫入到消息表中。

  • 業務系統讀取消息時,從消息表中讀取。

  • 日誌表表名爲 MQ_LOG,包含的字段:日誌 ID、發佈者信息、發佈時間、隊列名稱、消息內容。

  • 消息表表名就是隊列名稱,包含的字段:消息 ID(遞增生成)、消息內容、消息發佈時間、消息發佈者。

  • 日誌表需要及時清除已經寫入消息表的日誌數據,消息表最多保存 30 天的消息數據。

2. 細化設計點 2:數據如何複製?

直接採用 MySQL 主從複製即可,只複製消息存儲表,不復制日誌表。

3. 細化設計點 3:主備服務器如何倒換?

採用 ZooKeeper 來做主備決策,主備服務器都連接到 ZooKeeper 建立自己的節點,主服務器的路徑規則爲“/MQ/server/ 分區編號 /master”,備機爲“/MQ/server/ 分區編號 /slave”,節點類型爲 EPHEMERAL。

備機監聽主機的節點消息,當發現主服務器節點斷連後,備服務器修改自己的狀態,對外提供消息讀取服務。

4. 細化設計點 4:業務服務器如何寫入消息?

  • 消息隊列系統設計兩個角色:生產者和消費者,每個角色都有唯一的名稱。

  • 消息隊列系統提供 SDK 供各業務系統調用,SDK 從配置中讀取所有消息隊列系統的服務器信息,SDK 採取輪詢算法發起消息寫入請求給主服務器。如果某個主服務器無響應或者返回錯誤,SDK 將發起請求發送到下一臺服務器。

5. 細化設計點 5:業務服務器如何讀取消息?

  • 消息隊列系統提供 SDK 供各業務系統調用,SDK 從配置中讀取所有消息隊列系統的服務器信息,輪流向所有服務器發起消息讀取請求。

  • 消息隊列服務器需要記錄每個消費者的消費狀態,即當前消費者已經讀取到了哪條消息,當收到消息讀取請求時,返回下一條未被讀取的消息給消費者。

6. 細化設計點 6:業務服務器和消息隊列服務器之間的通信協議如何設計?

考慮到消息隊列系統後續可能會對接多種不同編程語言編寫的系統,爲了提升兼容性,傳輸協議用 TCP,數據格式爲 ProtocolBuffer。

當然還有更多設計細節就不再一一列舉,因此這還不是一個完整的設計方案,我希望可以通過這些具體實例來說明細化方案具體如何去做。

小結

今天我爲你講了架構設計流程的第四個步驟:詳細方案設計,並且基於模擬的“前浪微博”消息隊列系統,給出了具體的詳細設計示例,希望對你有所幫助。這個示例並不完整,有興趣的同學可以自己再詳細思考一下還有哪些細節可以繼續完善。

這就是今天的全部內容,留一道思考題給你吧,你見過“PPT 架構師”麼?他們一般都具備什麼特點?

歡迎你把答案寫到留言區,和我一起討論。相信經過深度思考的回答,也會讓你對知識的理解更加深刻。(編輯亂入:精彩的留言有機會獲得豐厚福利哦!)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章