12 | 架構設計流程:評估和選擇備選方案

此爲筆記

課程鏈接

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


上一期我講了設計備選方案,在完成備選方案設計後,如何挑選出最終的方案也是一個很大的挑戰,主要原因有:

  • 每個方案都是可行的,如果方案不可行就根本不應該作爲備選方案。

  • 沒有哪個方案是完美的。例如,A 方案有性能的缺點,B 方案有成本的缺點,C 方案有新技術不成熟的風險。

  • 評價標準主觀性比較強,比如設計師說 A 方案比 B 方案複雜,但另外一個設計師可能會認爲差不多,因爲比較難將“複雜”一詞進行量化。因此,方案評審的時候我們經常會遇到幾個設計師針對某個方案或者某個技術點爭論得面紅耳赤。

正因爲選擇備選方案存在這些困難,所以實踐中很多設計師或者架構師就採取了下面幾種指導思想:

  • 最簡派

設計師挑選一個看起來最簡單的方案。例如,我們要做全文搜索功能,方案 1 基於 MySQL,方案 2 基於 Elasticsearch。MySQL 的查詢功能比較簡單,而 Elasticsearch 的倒排索引設計要複雜得多,寫入數據到 Elasticsearch,要設計 Elasticsearch 的索引,要設計 Elasticsearch 的分佈式……全套下來複雜度很高,所以乾脆就挑選 MySQL 來做吧。

  • 最牛派

最牛派的做法和最簡派正好相反,設計師會傾向於挑選技術上看起來最牛的方案。例如,性能最高的、可用性最好的、功能最強大的,或者淘寶用的、微信開源的、Google 出品的等。

我們以緩存方案中的 Memcache 和 Redis 爲例,假如我們要挑選一個搭配 MySQL 使用的緩存,Memcache 是純內存緩存,支持基於一致性 hash 的集羣;而 Redis 同時支持持久化、支持數據字典、支持主備、支持集羣,看起來比 Memcache 好很多啊,所以就選 Redis 好了。

  • 最熟派

設計師基於自己的過往經驗,挑選自己最熟悉的方案。我以編程語言爲例,假如設計師曾經是一個 C++ 經驗豐富的開發人員,現在要設計一個運維管理系統,由於對 Python 或者 Ruby on Rails 不熟悉,因此繼續選擇 C++ 來做運維管理系統。

  • 領導派

領導派就更加聰明瞭,列出備選方案,設計師自己拿捏不定,然後就讓領導來定奪,反正最後方案選的對那是領導厲害,方案選的不對怎麼辦?那也是領導“背鍋”。

其實這些不同的做法本身並不存在絕對的正確或者絕對的錯誤,關鍵是不同的場景應該採取不同的方式。也就是說,有時候我們要挑選最簡單的方案,有時候要挑選最優秀的方案,有時候要挑選最熟悉的方案,甚至有時候真的要領導拍板。因此關鍵問題是:這裏的“有時候”到底應該怎麼判斷?今天我就來講講架構設計流程的第 3 步:評估和選擇備選方案。

架構設計第 3 步:評估和選擇備選方案

前面提到了那麼多指導思想,真正應該選擇哪種方法來評估和選擇備選方案呢?我的答案就是“360 度環評”!具體的操作方式爲:列出我們需要關注的質量屬性點,然後分別從這些質量屬性的維度去評估每個方案,再綜合挑選適合當時情況的最優方案

常見的方案質量屬性點有:性能、可用性、硬件成本、項目投入、複雜度、安全性、可擴展性等。在評估這些質量屬性時,需要遵循架構設計原則 1“合適原則”和原則 2“簡單原則”,避免貪大求全,基本上某個質量屬性能夠滿足一定時期內業務發展就可以了。

假如我們做一個購物網站,現在的 TPS 是 1000,如果我們預期 1 年內能夠發展到 TPS 2000(業務一年翻倍已經是很好的情況了),在評估方案的性能時,只要能超過 2000 的都是合適的方案,而不是說淘寶的網站 TPS 是每秒 10 萬,我們的購物網站就要按照淘寶的標準也實現 TPS 10 萬。

有的設計師會有這樣的擔心:如果我們運氣真的很好,業務直接一年翻了 10 倍,TPS 從 1000 上升到 10000,那豈不是按照 TPS 2000 做的方案不合適了,又要重新做方案?

這種情況確實有可能存在,但概率很小,如果每次做方案都考慮這種小概率事件,我們的方案會出現過度設計,導致投入浪費。考慮這個問題的時候,需要遵循架構設計原則 3“演化原則”,避免過度設計、一步到位的想法。按照原則 3 的思想,即使真的出現這種情況,那就算是重新做方案,代價也是可以接受的,因爲業務如此迅猛發展,錢和人都不是問題。例如,淘寶和微信的發展歷程中,有過多次這樣大規模重構系統的經歷。

通常情況下,如果某個質量屬性評估和業務發展有關係(例如,性能、硬件成本等),需要評估未來業務發展的規模時,一種簡單的方式是將當前的業務規模乘以 2 ~4 即可,如果現在的基數較低,可以乘以 4;如果現在基數較高,可以乘以 2。例如,現在的 TPS 是 1000,則按照 TPS 4000 來設計方案;如果現在 TPS 是 10000,則按照 TPS 20000 來設計方案。

當然,最理想的情況是設計一個方案,能夠簡單地擴容就能夠跟上業務的發展。例如,我們設計一個方案,TPS 2000 的時候只要 2 臺機器,TPS 20000 的時候只需要簡單地將機器擴展到 20 臺即可。但現實往往沒那麼理想,因爲量變會引起質變,具體哪些地方質變,是很難提前很長時間能預判到的。舉一個最簡單的例子:一個開發團隊 5 個人開發了一套系統,能夠從 TPS 2000 平滑擴容到 TPS 20000,但是當業務規模真的達到 TPS 20000 的時候,團隊規模已經擴大到了 20 個人,此時系統發生了兩個質變:

  • 首先是團隊規模擴大,20 個人的團隊在同一個系統上開發,開發效率變將很低,系統迭代速度很慢,經常出現某個功能開發完了要等另外的功能開發完成才能一起測試上線,此時如果要解決問題,就需要將系統拆分爲更多子系統。

  • 其次是原來單機房的集羣設計不滿足業務需求了,需要升級爲異地多活的架構。

如果團隊一開始就預測到這兩個問題,系統架構提前就拆分爲多個子系統並且支持異地多活呢?這種“事後諸葛亮”也是不行的,因爲最開始的時候團隊只有 5 個人,5 個人在有限的時間內要完成後來 20 個人才能完成的高性能、異地多活、可擴展的架構,項目時間會遙遙無期,業務很難等待那麼長的時間。

完成方案的 360 度環評後,我們可以基於評估結果整理出 360 度環評表,一目瞭然地看到各個方案的優劣點。但是 360 度環評表也只能幫助我們分析各個備選方案,還是沒有告訴我們具體選哪個方案,原因就在於沒有哪個方案是完美的,極少出現某個方案在所有對比維度上都是最優的。例如:引入開源方案工作量小,但是可運維性和可擴展性差;自研工作量大,但是可運維和可維護性好;使用 C 語言開發性能高,但是目前團隊 C 語言技術積累少;使用 Java 技術積累多,但是性能沒有 C 語言開發高,成本會高一些……諸如此類。

面臨這種選擇上的困難,有幾種看似正確但實際錯誤的做法。

  • 數量對比法:簡單地看哪個方案的優點多就選哪個。例如,總共 5 個質量屬性的對比,其中 A 方案佔優的有 3 個,B 方案佔優的有 2 個,所以就挑選 A 方案。

這種方案主要的問題在於把所有質量屬性的重要性等同,而沒有考慮質量屬性的優先級。例如,對於 BAT 這類公司來說,方案的成本都不是問題,可用性和可擴展性比成本要更重要得多;但對於創業公司來說,成本可能就會變得很重要。

其次,有時候會出現兩個方案的優點數量是一樣的情況。例如,我們對比 6 個質量屬性,很可能出現兩個方案各有 3 個優點,這種情況下也沒法選;如果爲了數量上的不對稱,強行再增加一個質量屬性進行對比,這個最後增加的不重要的屬性反而成了影響方案選擇的關鍵因素,這又犯了沒有區分質量屬性的優先級的問題。

  • 加權法:每個質量屬性給一個權重。例如,性能的權重高中低分別得 10 分、5 分、3 分,成本權重高中低分別是 5 分、3 分、1 分,然後將每個方案的權重得分加起來,最後看哪個方案的權重得分最高就選哪個。

這種方案主要的問題是無法客觀地給出每個質量屬性的權重得分。例如,性能權重得分爲何是 10 分、5 分、3 分,而不是 5 分、3 分、1 分,或者是 100 分、80 分、60 分?這個分數是很難確定的,沒有明確的標準,甚至會出現爲了選某個方案,設計師故意將某些權重分值調高而降低另外一些權重分值,最後方案的選擇就變成了一個數字遊戲了。

正確的做法是按優先級選擇,即架構師綜合當前的業務發展情況、團隊人員規模和技能、業務發展預測等因素,將質量屬性按照優先級排序,首先挑選滿足第一優先級的,如果方案都滿足,那就再看第二優先級……以此類推。那會不會出現兩個或者多個方案,每個質量屬性的優缺點都一樣的情況呢?理論上是可能的,但實際上是不可能的。前面我提到,在做備選方案設計時,不同的備選方案之間的差異要比較明顯,差異明顯的備選方案不可能所有的優缺點都是一樣的。

評估和選擇備選方案實戰

再回到我們設計的場景“前浪微博”。針對上期提出的 3 個備選方案,架構師組織了備選方案評審會議,參加的人有研發、測試、運維、還有幾個核心業務的主管。

1. 備選方案 1:採用開源 Kafka 方案

  • 業務主管傾向於採用 Kafka 方案,因爲 Kafka 已經比較成熟,各個業務團隊或多或少都瞭解過 Kafka。

  • 中間件團隊部分研發人員也支持使用 Kafka,因爲使用 Kafka 能節省大量的開發投入;但部分人員認爲 Kafka 可能並不適合我們的業務場景,因爲 Kafka 的設計目的是爲了支撐大容量的日誌消息傳輸,而我們的消息隊列是爲了業務數據的可靠傳輸。

  • 運維代表提出了強烈的反對意見:首先,Kafka 是 Scala 語言編寫的,運維團隊沒有維護 Scala 語言開發的系統的經驗,出問題後很難快速處理;其次,目前運維團隊已經有一套成熟的運維體系,包括部署、監控、應急等,使用 Kafka 無法融入這套體系,需要單獨投入運維人力。

  • 測試代表也傾向於引入 Kafka,因爲 Kafka 比較成熟,無須太多測試投入。

2. 備選方案 2:集羣 + MySQL 存儲

  • 中間件團隊的研發人員認爲這個方案比較簡單,但部分研發人員對於這個方案的性能持懷疑態度,畢竟使用 MySQL 來存儲消息數據,性能肯定不如使用文件系統;並且有的研發人員擔心做這樣的方案是否會影響中間件團隊的技術聲譽,畢竟用 MySQL 來做消息隊列,看起來比較“土”、比較另類。

  • 運維代表贊同這個方案,因爲這個方案可以融入到現有的運維體系中,而且使用 MySQL 存儲數據,可靠性有保證,運維團隊也有豐富的 MySQL 運維經驗;但運維團隊認爲這個方案的成本比較高,一個數據分組就需要 4 臺機器(2 臺服務器 + 2 臺數據庫)。

  • 測試代表認爲這個方案測試人力投入較大,包括功能測試、性能測試、可靠性測試等都需要大量地投入人力。

  • 業務主管對這個方案既不肯定也不否定,因爲反正都不是業務團隊來投入人力來開發,系統維護也是中間件團隊負責,對業務團隊來說,只要保證消息隊列系統穩定和可靠即可。

3. 備選方案 3:集羣 + 自研存儲系統

  • 中間件團隊部分研發人員認爲這是一個很好的方案,既能夠展現中間件團隊的技術實力,性能上相比 MySQL 也要高;但另外的研發人員認爲這個方案複雜度太高,按照目前的團隊人力和技術實力,要做到穩定可靠的存儲系統,需要耗時較長的迭代,這個過程中消息隊列系統可能因爲存儲出現嚴重問題,例如文件損壞導致丟失大量數據。

  • 運維代表不太贊成這個方案,因爲運維之前遇到過幾次類似的存儲系統故障導致數據丟失的問題,損失慘重。例如,MongoDB 丟數據、Tokyo Tyrant 丟數據無法恢復等。運維團隊並不相信目前的中間件團隊的技術實力足以支撐自己研發一個存儲系統(這讓中間件團隊的人員感覺有點不爽)。

  • 測試代表贊同運維代表的意見,並且自研存儲系統的測試難度也很高,投入也很大。

  • 業務主管對自研存儲系統也持保留意見,因爲從歷史經驗來看,新系統上線肯定有 bug,而存儲系統出 bug 是最嚴重的,一旦出 bug 導致大量消息丟失,對系統的影響會嚴重。

針對 3 個備選方案的討論初步完成後,架構師列出了 3 個方案的 360 度環評表:

列出這個表格後,無法一眼看出具體哪個方案更合適,於是大家都把目光投向架構師,決策的壓力現在集中在架構師身上了。

架構師經過思考後,給出了最終選擇備選方案 2,原因有:

  • 排除備選方案 1 的主要原因是可運維性,因爲再成熟的系統,上線後都可能出問題,如果出問題無法快速解決,則無法滿足業務的需求;並且 Kafka 的主要設計目標是高性能日誌傳輸,而我們的消息隊列設計的主要目標是業務消息的可靠傳輸。

  • 排除備選方案 3 的主要原因是複雜度,目前團隊技術實力和人員規模(總共 6 人,還有其他中間件系統需要開發和維護)無法支撐自研存儲系統(參考架構設計原則 2:簡單原則)。

  • 備選方案 2 的優點就是複雜度不高,也可以很好地融入現有運維體系,可靠性也有保障。

針對備選方案 2 的缺點,架構師解釋是:

  • 備選方案 2 的第一個缺點是性能,業務目前需要的性能並不是非常高,方案 2 能夠滿足,即使後面性能需求增加,方案 2 的數據分組方案也能夠平行擴展進行支撐(參考架構設計原則 3:演化原則)。

  • 備選方案 2 的第二個缺點是成本,一個分組就需要 4 臺機器,支撐目前的業務需求可能需要 12 臺服務器,但實際上備機(包括服務器和數據庫)主要用作備份,可以和其他系統並行部署在同一臺機器上。

  • 備選方案 2 的第三個缺點是技術上看起來並不很優越,但我們的設計目的不是爲了證明自己(參考架構設計原則 1:合適原則),而是更快更好地滿足業務需求。

最後,大家針對一些細節再次討論後,確定了選擇備選方案 2。

通過“前浪微博”這個案例我們可以看出,備選方案的選擇和很多因素相關,並不單單考慮性能高低、技術是否優越這些純技術因素。業務的需求特點、運維團隊的經驗、已有的技術體系、團隊人員的技術水平都會影響備選方案的選擇。因此,同樣是上述 3 個備選方案,有的團隊會選擇引入 Kafka(例如,很多創業公司的初創團隊,人手不夠,需要快速上線支撐業務),有的會選擇自研存儲系統(例如,阿里開發了 RocketMQ,人多力量大,業務複雜是主要原因)。

小結

今天我爲你講了架構設計流程的第三個步驟:評估和選擇備選方案,並且基於模擬的“前浪微博”消息隊列系統,給出了具體的評估和選擇示例,希望對你有所幫助。

這就是今天的全部內容,留一道思考題給你吧,RocketMQ 和 Kafka 有什麼區別,阿里爲何選擇了自己開發 RocketMQ?

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

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