陳皓:我做系統架構的原則

本文是 MegaEase 創始人陳皓自己多年的從事計算軟件職業的中對於軟件系統架構的一些原則總結,其中有很多他的經歷和想法。他個人的話如下:

工作 20 年來看到了很多公司的很多的系統架構,相關的經歷也越來越多,所以,逐漸形成了自己的邏輯和方法論。寫下這篇文章,除了總結個人的經驗和想法,供人蔘考和借鑑。同時,也是針對於現有市面上衆多不合理的架構和方案,也作一種 “糾正”……

https://twitter.com/haoel/status/1473202816114376710?s=20

下面是文章正文。

工作 20 多年了,這 20 來年看到了很多公司系統架構,也看到了很多問題,在跟這些公司進行交流和討論的時候,包括進行實施和方案比較的時候,都有很多各種方案的比較和妥協,因爲相關的經歷越來越多,所以,逐漸形成了自己的邏輯和方法論。今天,想寫下這篇文章,把我的這些個人的經驗和想法總結下來,希望能夠讓更多的人可以參考和借鑑,並能夠做出更好的架構來。另外,我的這些思維方式和原則都針對於現有市面上衆多不合理的架構和方案,所以,也算是一種 “糾正”……(注意,這篇文章所說的這些架構上的原則,一般適用於相對比較複雜的業務,如果只是一些簡單和訪問量不大的應用,那麼你可能會得出相反的結論)

目錄

原則一:關注真正的收益而不是技術本身

原則二:以應用服務和 API 爲視角

原則三:選擇最主流和成熟的技術

原則四:完備性會比性能更重要

原則五:制定並遵循服從標準、規範和最佳實踐

原則六:重視架構擴展性和可運維性

原則七:對控制邏輯進行全面收口

原則八:不要遷就老舊系統的技術債務

原則九:別依賴自己的經驗,要基於數據和學習

原則十:千萬要小心 X–Y 問題,要追問原始需求

原則十一:激進勝於保守,創新與實用並不衝突

原則一

關注真正的收益而不是技術本身

對於軟件架構來說,我覺得第一重要的是架構的收益,如果不說收益,只是爲了技術而技術,而沒有任何意義。對於技術收益來說,我覺得下面這幾個收益是非常重要的:

如果一個系統架構不能在上面三個事上起到作用,那就沒有意義了。

原則二

以服務和 API 爲視角,而非資源和技術

國內很多公司都會有很多分工,基本上都會分成運維和開發,運維又會分成基礎運維和應用運維,開發則會分成基礎核心開發和業務開發。不同的分工會導致完全不同的視角和出發點。比如,基礎運維和開發的同學更多的只是關注資源的利用率和性能,而應用運維和業務開發則更多關注的是應用和服務上的東西。這兩者本來相關無事,但是因爲分佈式架構的演進,導致有一些系統已經說不清楚是基礎層的還是應用層的了,比如像服務治理上的東西,裏面即有底層基礎技術,也需要業務的同學來配合,包括 k8s 也樣,裏面即有底層的如網絡這樣的技術,也有需要業務配合的 readniess 和 liveness 這樣的健康檢查,以及業務應用需要 configMap 等等 ……

這些東西都讓我感覺到所謂 DevOps,其實就是因爲很多技術和組件已經分不清是 Dev 還是 Ops 的了,所以,需要合併 Dev 和 Ops。而且,整個組織和架構的優化,已經不能通過調優單一分工或是單一組件能夠有很大提升的了。其需要有一種自頂向下的,整體規劃,統一設計的方式,才能做到整體的提升(可以試想一下城市交通的優化,當城市規模到一定程度的時候,整體的性能你是無法通過優化幾條路或是幾條街區來完成的,你需要對整個城市做整體的功能體的規劃纔可能達到整體效率的提升)。而爲了做到整體的提升,需要所有的人都要有一個統一的視角和目標,這幾年來,我覺得這個目標就是——要站在服務和 對外 API 的視角來看問題,而不是技術和底層的角度。

原則三

選擇最主流和成熟的技術

技術選型是一件很重要的事,技術一旦選錯,那會導致整個架構需要做調整,而對架構的調整重來都不是一件簡單的事,我在過去幾年內,當系統越來越複雜的時候,用戶把他們的  PHP,Python, .NET,或 Node.js 的架構完全都遷移到 Java + Go 的架構上來的案例不斷的發生。這個過程還是非常痛苦的,但是你沒有辦法,當你的系統越來越複雜,越來越大時,你就再也不能在一些玩具技術上玩了,你需要的更爲工業化的技術。

在我見過的公司中,好些公司的架構都被技術負責人個人的喜好、擅長和個人經驗給綁架了,完全不是從一個客觀的角度來進行技術選型。其實,從 0 到 1 的階段,你用什麼樣的技術都行,如果你做一個簡單的應用,沒有事務處理沒有複雜的交易流程,比如一些論壇、社交之類的應用,你用任何語言都行。但是如果有一天你的系統變複雜了,需要處理交易了,量也上來了,從 1 到 10,甚至從 10 到 100,你的開發團隊也變大了,需要構建的系統越來越大,你可能會發現你只有一個選擇,就是 Java。想想京東從. NET 到 Java,淘寶從 PHP 到 Java……

注,一些有主觀喜好的人一定會對我上述對 Java 的描述感到不適,我用一些證據說明一下——全中國所有的電商平臺,幾百家銀行,三大電信運營商,所有的保險公司,劵商的系統,醫院裏的系統,電子政府系統,等等,基本都是用 Java 開發的,包括 AWS 的主流語言也是 Java,阿里雲一開始用 C++/Python 寫控制系統,後面也開始用 Java …… 你可能會說 B 站是用 go 語言,但是你可能不知道 B 站的電商和大數據是用 Java…… 懂着數據分析的同學,建議上各大招聘網站上搜一下 Java 的職位數量,你就知道某個技術是否主流和熱門……

原則四

完備性會比性能更重要

我發現好些公司的架構師做架構的時候,首要考慮的是架構的性能是否能夠撐得住多大多大的流量,而不是考慮系統的完備性和擴展性。所以,我已經多次見過這樣的案例了,一開始直接使用 MongoDB 這樣的非關係型數據庫,或是把數據直接放在 Redis 裏,而直接放棄關係型數據庫的數據完備性的模型,而在後來需要在數據上進行關係查詢的時候,發現 NoSQL 的數據庫在 Join 上都表現的太差,然後就開始各種飛線,爲了不做 Join 就開始冗餘數據,然而自己又維護不好冗餘數據後帶來的數據一致性的問題,導致數據上的各種錯亂丟失。

所以,我給如下的一些如下的架構原則:

爲了追求所謂的性能,把整個系統的完備性丟失掉,相當地得不償失。

原則五

制定並遵循服從標準、規範和最佳實踐

這個原則是非常重要的,因爲只有服從了標準,你的架構才能夠有更好的擴展性。比如:我經常性的見到很多公司的系統既沒有服從業界標準,也沒有形成自己公司的標準,感覺就像一羣烏合之衆一樣。最典型的例子就是 HTTP 調用的狀態返回碼。業內給你的標準是 200 表示成功,3xx 跳轉,4xx 表示調用端出錯,5xx 表示服務端出錯,我實在是不明白爲什麼無論成功和失敗大家都喜歡返回 200,然後在 body 裏指出是否 error(前兩年我在微信公衆號裏看到一個有一定名氣的互聯網老兵推薦使用無論正確還是出錯都返回 200 的做法,我在後臺再三確認後,我發現這樣的架構師真是害人不淺)。這樣做最大的問題是——監控系統將在一種低效的狀態下工作。監控系統需要把所有的網絡請求包打開後才知道是否是錯誤,而且完全不知道是調用端出錯還是服務端出錯,於是一些像重試或熔斷這樣的控制系統完全不知道怎麼搞(如果是 4xx 錯,那麼重試或熔斷是沒有意義的,只有 5xx 纔有意義)。有時候,我會有種越活越退步的感覺,錯誤碼設計這種最基本最基礎的東西爲什麼會沒有?並且一個公司會任由着大家亂來?這些基礎技能怎麼就這樣丟掉了?

還有,我還見過一些公司,他們整個組織沒有一個統一的用戶 ID 的設計,各個系統之間同步用戶的數據是通過用戶的身份證 ID,是的,就是現實世界的身份證 ID,包括在網關上設置的用戶白名單居然也是用身份證 ID。我對這個公司的內的用戶隱私管理有很大的擔憂。一個企業,一個組織,如果沒有標準和規範,也就會有抽象,這一定是要出各種亂子的。

下面,我羅列一些你需要注意的標準和規範(包括但不限於):

這裏重要說一下幾個事:

原則六

重視架構擴展性和可運維性

在我見過很多架構裏,技術人員只考慮當下,但從來不考慮系統的未來擴展性和可運維性。所謂的管生不管養。如果你生下來的孩子胳膊少腿,嚴重畸形,那麼未來是很難玩的。因爲架構不是和軟件不是寫好就完的,是需要不斷修改不斷維護的,80% 的軟件成本都是在維護上。所以,如何讓你的架構有更好的擴展性,可以更容易地運維,這個是比較重要的。所謂的擴展性,意味着,我可以很容易地加更多的功能,或是加入更多的系統,而所謂可運維,就是說我可以對線上的系統做任意的變更。擴展性要求的是有標準規範且不耦合的業務架構,可運維性要求的則是可控的能力,也就是一組各式各樣的控制系統。

原則七

對控制邏輯進行全面收口

所有的程序都會有兩種邏輯,一種是業務邏輯,一種是控制邏輯,業務邏輯就是完成業務的邏輯,控制邏輯是輔助,比如你用多線程,還是用分佈式,是用數據庫還是用文件,如何配置、部署,運維、監控,事務控制,服務發現,彈性伸縮,灰度發佈,高併發,等等,等等 …… 這些都是控制邏輯,跟業務邏輯沒有一毛錢關係。控制邏輯的技術深度會通常會比業務邏輯要深一些,門檻也會要高一些,所以,最好要專業的程序員來負責控制邏輯的開發,統一規劃統一管理,進行收口。這其中包括:

對此,這裏的原則是:

原則八

不要遷就老舊系統的技術債務

我發現很多公司都很非常大的技術債務,這些債務具體表現如下:

來找我尋求技術幫助的人都有各種各樣的問題。我都會對他們苦口婆心地說同樣的一句話——“如果你是來找我 case-by-case 解決問題,我興趣不大,因爲,你們千萬不要寄希望能夠很簡單的把一輛夏利車改成一輛法拉利跑車,或是把一棟地基沒打好的歪樓搞正。以前欠下的技術債,都得要還,沒打好的地基要重新打,沒建配套設施都要建。這些基礎設施如果不按照正確科學的方式建立的話,你是不可能有一個好的的系統,我也沒辦法幫你 case-by-case 的解決問題……”,一開始,他們都會對我說,沒問題,我們就是要還債,但是,最後發現要還的債真多,有點承受不了,就開始現原形了。

他們開始爲自己的 “欠的技術債” 找各種合理化的理由——給你解釋各種各樣的歷史原因和不得以而爲之的理由。談着談着,讓我有一種感覺——他們希望得到一種什麼都不改什麼都不付出的方式就可以進步的心態,他們寧可讓新的技術 low 下來遷就於這些技術債,把新的技術濫用地亂七八糟的。有一個公司,他們的系統架構和技術選型基本都搞錯了,使用錯誤的模型構建系統,導致整個系統的性能非常之差,也才幾千萬條數據,但他們想的不是還債,不是把地基和配套設施建好,而且要把樓修的更高,上更多的系統——他們覺得現有的系統挺好,性能問題的原因是他們沒一個大數據平臺,所以要建大數據平臺……

我見過很多很多公司,包括大如 BAT 這樣的公司,都會在原來的技術債上進行更多的建設,然後,技術債越來越大,利息越來越大,最終成爲一個高利貸,再也還不了(我在《開發團隊的效率》一文中講過一個 WatchDog 的架構模式,一個系統爛了,不是去改這個系統,而是在旁邊建一個系統來看着它,我很難理解爲什麼會有這樣的邏輯,也許是爲了要解決更多的就業……)

這裏有幾個原則和方法我是非常堅持的,分享給大家:

原則九

別依賴自己的經驗,要基於數據和學習

有好些人來找我跟我說他們的技術問題,然後希望我能夠給他們一個答案。我說,我需要了解一下你現有系統的情況,也就是需要先做個診斷,我只有得到這些數據後,我纔可能明白真正的原因是什麼 ,我纔可能給你做出一個比較好的技術方案。我個人覺得這是一種對對方負責的方法,因爲技術手段太多了,所有的技術手段都有適應的場景,並且有各種 trade-off,所以,只有調研完後才能做出決定。這跟醫生看病是一樣的,確診病因不能靠經驗,還是要靠診斷數據。在科學麪前,所有的經驗都是靠不住的……

另外,如果有一天你在做技術決定的時候,開始憑自己以往的經驗,那麼你就已經不可能再成長了。人都是不可能通過不斷重複過去而進步的,人的進步從來都是通過學習自己不知道的東西。所以,千萬不要依賴於自己的經驗做決定。做任何決定之前,最好花上一點時間,上網查一下相關的資料,技術博客,文章,論文等 ,同時,也看看各個公司,或是各個開源軟件他們是怎麼做的?然後,比較多種方案的 Pros/Cons,最終形成自己的決定,這樣,纔可能做出一個更好的決定。

原則十

千萬要小心 X–Y 問題,要追問原始需求

對於 X-Y 問題,也就是說,用戶爲了解決 X 問題,他覺得用 Y 可以解,於是問我 Y 怎麼搞,結果搞到最後,發現原來要解決的 X 問題,這個時候最好的解決方案不是 Y,而是 Z。這種 X-Y 問題真是相當之多,見的太多太多了。所以,每次用戶來找我的時候,我都要不斷地追問什麼是 X 問題。

比如,好些用戶都會來問我他們要一個大數據流式處理,結果追問具體要解決什麼樣的問題時,才發現他們的問題是因爲服務中有大量的狀態,需要把相同用戶的數據請求放在同一個服務上處理,而且設計上導致一個慢函數拖慢整個應用服務。最終就是做一下性能調優就好了,根本沒有必要上什麼大數據的流式處理。

我很喜歡追問爲什麼 ,這種追問,會讓客戶也跟着來一起重新思考。比如,有個客戶來找我評估的一個技術架構的決定,從理論上來說,好像這個架構在用戶的這個場景下非常不錯。但是,這個場景和這個架構是我職業生涯從來沒有見過的。於是,我開始追問這個爲什麼會是這麼一個場景?當我追問的時候,我發現用戶都感到這個場景的各種不合理。最後引起了大家非常深刻的研討,最終用戶把那個場景修正後,而架構就突然就變成了一個常見且成熟的的模型……

原則十一

激進勝於保守,創新與實用並不衝突

我對技術的態度是比較激進的,但是,所謂的激進並不是瞎搞,也不是見新技術就上,而是積極擁抱會改變未來的新技術,如:Docker/Go,我就非常快地跟進,但是像區塊鏈或是 Rust 這樣的,我就不是很積極。因爲,其並沒有命中我認爲的技術趨勢的幾個特徵(參看《Go,Docker 和新技術 》)。當然,我也不是不喜歡的就不學了,我對區塊鏈和 Rust 我一樣學習,我也知道這些技術的優勢,但我不會大規模使用它們。另外,我也尊重保守的決定,這裏面沒有對和錯。但是,我個人覺得對技術激進的態度比起保守來說有太多的好處了。一方面來說,對於用戶來說,很大程度上來說,新技術通常都表面有很好的競爭力,而且我見太多這樣成功的公司都在積極擁抱新的技術的,而保守的通常來說都越來越不好。

有一些人會跟我說,我們是實用主義,我們不需要創新,能解決當下的問題就好,所以,我們不需要新技術,現有的技術用好就行了。這類的公司,他們的技術設計第一天就在負債,雖然可以解決當下問題,但是馬上就會出現新的問題,然後他們會疲於解決各種問題。最後呢,最後還是會走到新的技術上。

這裏的邏輯很簡單 —— 進步永遠來自於探索,探索是要付出代價的,但是收益更大。對我而言,不敢冒險纔是最大的冒險,不敢犯錯纔是最大的錯誤,害怕失去會讓你失去的更多……

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/rx9dqv2tg1Bo0TPPc_hPzQ