“騎手與大象”架構:超越微服務與單體之爭的務實之道?
在軟件架構的江湖里,關于“微服務”與“單體”的論戰(zhàn),幾乎從未停歇。一方推崇微服務的靈活性、可擴展性和獨立部署,另一方則堅守單體的簡潔性、低通信開銷和易于本地調(diào)試。近年來,我們甚至看到像亞馬遜 Prime Video 這樣重量級的玩家,也公開分享了其從微服務“回歸”到某種形式的單體(或者說更粗粒度的服務)的實踐,引發(fā)了業(yè)界新一輪的思考。
這不禁讓我們反問:微服務與單體,真的就是非此即彼的“二元對立”嗎?
最近,國外一家名為DealGate公司的一篇文章《Introducing the Rider and Elephant Software Architecture》,提出了一種他們稱之為“騎手與大象”的架構模式,試圖在這場看似無解的爭論中,找到一條務實的中間道路。這種模式不僅在他們的實踐中取得了顯著成效,其背后的設計哲學和對技術選型的思考,也頗具啟發(fā)意義。
“騎手與大象”:一個古老隱喻的現(xiàn)代架構演繹
DealGate 將其架構模式命名為“騎手與大象”,其靈感來源于心理學中的一個經(jīng)典比喻:人類的思維由兩部分組成——理性的“騎手”(對應我們發(fā)達的前額葉皮層,負責規(guī)劃、分析和決策)和感性的、更強大的“大象”(對應我們原始的、更底層的“蜥蜴腦”或“穴居人腦”,驅動著本能和情緒)。騎手雖然可以嘗試引導大象,但無法完全控制它;而如果騎手想獨自前行,又會發(fā)現(xiàn)大象的力量是其無法比擬的。只有當騎手與大象協(xié)同合作時,才能發(fā)揮出最大的效能。
在 DealGate 的架構中,這個隱喻被巧妙地映射到了技術組件上:
- “大象 (Elephant)”:由 Go語言構建的應用。它不包含任何復雜的業(yè)務邏輯,但卻承擔著所有“臟活累活”——大規(guī)模的、高并發(fā)的數(shù)據(jù)處理。在 DealGate 的場景中,這可能意味著在任何時刻都有數(shù)萬個 goroutine 在處理圖像、PDF,抓取數(shù)千萬級別的網(wǎng)頁,并在每個網(wǎng)頁上運行數(shù)千萬次的正則表達式匹配。“大象”的核心職責是:強大、高效、能扛事兒。
- “騎手 (Rider)”:由NextJS (Node.js) 構建的應用。它承載了所有的業(yè)務邏輯、數(shù)據(jù)庫訪問、用戶交互等。“騎手”的核心職責是:靈活、敏捷、快速響應業(yè)務變化。
- 韁繩 (Communication):“騎手”通過 gRPC 來“引導”和控制“大象”,兩者之間保持低開銷、高效率的通信。
這種架構的核心思想是:將需要極致性能和高并發(fā)處理的“重計算”部分(大象),與需要快速迭代和靈活業(yè)務邏輯的“輕應用”部分(騎手)進行分離,并讓它們通過高效的通信方式協(xié)同工作。
為何選擇“騎手與大象”?DealGate 的實踐與思考
DealGate 之所以采用這種架構,源于他們在實際業(yè)務中遇到的挑戰(zhàn)和對現(xiàn)有架構模式的反思。
- 對“微服務 vs 單體”的“虛假二分法”說不:他們認為,單純地在微服務和單體之間做選擇,往往忽略了業(yè)務的復雜性和多樣性。他們希望能夠“have the best of both worlds”(取兩者之長)。
- Node.js/NextJS 的局限性:盡管 DealGate 的主要應用是用 NextJS 編寫的,但他們發(fā)現(xiàn),即使 Node.js 在 I/O 和網(wǎng)絡處理上有多線程優(yōu)勢,其正則表達式等 CPU 密集型操作仍然受限于單線程(JavaScript 的執(zhí)行模型)。當需要在后臺進行大量正則匹配,同時還要響應 Web 應用請求時,性能瓶頸就顯而易見了。
- Go 語言的“大象”潛質(zhì):文章中明確指出:“Go語言非常適合這種場景,你可以輕松地扔給它數(shù)萬個CPU密集型進程,它會愉快地處理掉所有這些”。這充分肯定了 Go 語言在并發(fā)處理和性能方面的核心優(yōu)勢。
- 對微服務通信開銷的警惕:DealGate 批評了許多微服務架構使用 JSON 進行進程間通信的做法,認為其“序列化和反序列化開銷是令人發(fā)指的”。他們選擇 gRPC,正是為了最大限度地降低“騎手”與“大象”之間的通信成本,確保即使在需要傳輸大量數(shù)據(jù)(因為“大象”不包含業(yè)務邏輯,需要被視為“愚笨的工人”)的情況下,也能保持高效。
Go 語言:扮演“大象”的理想之選
在“騎手與大象”的架構中,Go 語言之所以被選中扮演“吃苦耐勞的大象”,并非偶然。這得益于 Go 語言的核心特性:
- 極致的并發(fā)性能:Goroutine 和 Channel 機制,配合高效的調(diào)度器,使得 Go 能夠輕松創(chuàng)建和管理海量的并發(fā)任務,這對于處理 DealGate 所述的“數(shù)萬個 goroutine 同時處理數(shù)據(jù)”的場景至關重要。
- 高效的執(zhí)行效率:Go 語言編譯為原生機器碼,其性能接近 C/C++,遠超解釋型語言,非常適合 CPU 密集型的數(shù)據(jù)處理任務。
- 強大的標準庫:Go 的標準庫提供了豐富的網(wǎng)絡編程、文本處理(包括正則表達式)、數(shù)據(jù)編解碼等功能,為構建“大象”應用提供了堅實的基礎。
- 簡潔的部署:Go 應用可以編譯成單個靜態(tài)鏈接的可執(zhí)行文件,部署簡單,依賴少。
可以說,Go 語言的設計哲學和核心能力,使其成為承載這種“無業(yè)務邏輯、高并發(fā)、重計算”角色的理想選擇。
語言選型的“二八原則”與“務實主義”
“騎手與大象”架構的另一個核心啟示,在于其對不同技術棧的選擇策略,體現(xiàn)了一種深刻的“務實主義”和對“成本效益”的考量。
文章明確反駁了“既然有更高性能的語言(如 Rust 或 Go 本身),為什么不把所有應用都用它來寫?”的觀點,并將其類比為“那所有應用都應該用匯編來寫了”。
其核心邏輯是:
- 高級語言(如 JavaScript, Python)的優(yōu)勢:更安全(內(nèi)存管理等)、生產(chǎn)力更高(表達力強、語法糖和輪子多)、開發(fā)者社群更大、單位時間開發(fā)成本相對更低。
- 高性能/底層語言(如 Go, Rust, C++)的優(yōu)勢:性能極致、對系統(tǒng)資源有更精細的控制。但通常也意味著更陡峭的學習曲線、更高的開發(fā)成本、以及(在某些情況下)更長的開發(fā)周期。
DealGate 的策略是:“在你必須快的地方快,其他一切都選擇高級語言和(相對)單體的模式。” 這意味著:
- 將昂貴的、需要精細優(yōu)化的高性能代碼(大象)限制在最小的必要范圍內(nèi)(例如,只占整個業(yè)務系統(tǒng)的 10%)。
- 將大部分的業(yè)務邏輯、用戶交互(騎手)用生產(chǎn)力更高、開發(fā)更快的高級語言來實現(xiàn)。
這種“混合編程”或“多語言架構”的思路,實際上是在性能、開發(fā)效率、人才獲取成本、維護成本等多個維度之間進行權衡和優(yōu)化。它提醒我們,技術選型不應盲目追求“最新最酷”或“性能極致”,而應服務于業(yè)務需求,并充分考慮團隊和公司的實際情況。
文章中也提及了對“Just write Rust”(就用 Rust 寫)這類口號的反思,指出大多數(shù)公司和開發(fā)者可能無法承擔全員學習和使用像 Rust 這樣“高門檻”語言的成本。這并非否定 Rust 的優(yōu)秀,而是強調(diào)技術選型的現(xiàn)實約束。
小結:“沒有完美的解決方案,只有明智的權衡”
“沒有完美的解決方案,只有權衡取舍”。DealGate 的文章以這句經(jīng)典的名言作為總結,恰如其分。
“騎手與大象”架構,正是在微服務的靈活性、分布式能力與單體的低心智負擔、高開發(fā)效率之間做出的一種明智權衡。它并非適用于所有場景的“銀彈”,但在類似 DealGate 這樣需要處理大規(guī)模數(shù)據(jù)密集型任務,同時又需要快速迭代業(yè)務邏輯的場景下,無疑提供了一種極具價值的、務實的架構思路。
它也再次印證了一個樸素的道理:優(yōu)秀的架構設計,往往不是對某種“主義”的盲從,而是對業(yè)務需求的深刻理解和對不同技術優(yōu)劣的精準把握,最終在各種約束條件下找到那個“恰到好處”的平衡點。
或許,在微服務與單體的喧囂爭論之外,我們更應該學習這種“騎手與大象”的智慧——在正確的地方,用正確的方式,做正確的事情。
參考文獻: Introducing the Rider and Elephant Software Architecture - https://d-gate.io/blog/rider-and-elephant-architecture