爲什麼拆微服務這麼艱難

微服務,這三個字正在席捲着目前的互聯網軟件行業,尤其在近幾年雲原生迸發後,似乎人人都對微服務有了更廣泛的使用和理解,張口就是各種各樣的問號,有着強大的好奇心。

無獨有偶,我有一個朋友鯉魚在內部微服務的早期(每個業務組起步)就經常遇到下述的對話:

  1. 張三:爲什麼要拆現在的代碼?

  2. 鯉魚:因爲 !@)&&#@!)&#!&)@!&!的原因。

  3. 張三:那即將要做的 “微服務” 是按照什麼維度去拆分的服務?

  4. 鯉魚:常見的一般根據 !@#*@!#&!(@&!@)#@ 的方式來拆分。

  5. 張三:照你這麼說好像也不大對,我看每個業務組拆分的維度似乎都不大一樣?

  6. 鯉魚:嗯,每個業務組還有自己的見解,不會完全相同。

  7. 張三:。。。所以微服務的拆分維度到底是什麼?

爲什麼想拆


爲什麼張三會有這個疑問呢,實際上是因爲研發內部希望從原先的大單體,大倉庫向微服務體系拆分轉換,其原先大單體倉庫結構,類 Monorepo:

但類 Monorepo 又有不少的問題,像是:

  1. 單個 Repo 體積過大:導致 Git 無法直接拉取。當你設置完再拉取時,在網速慢時還能去泡杯咖啡,並且在開發機性能不佳的情況下,IDE 會比較卡,代碼運行起來也慢。

  2. 單個 Repo 存在公共函數 / SDK:在代碼倉庫中,必然存在公共依賴。因此在解決代碼衝突時,若遺留了衝突符,且在動態語言中,不涉及便運行正常。但其實在上線後卻又影響到其他業務,可真是糟糕透頂,分分鐘被迫抱着事故。

  3. 單個 Repo 模塊職責 / 邊界不清:在實際的軟件開發中,涉及數十個業務組同時在一個大 Repo 下進行開發,沒有強控邊界的情況下,往往會逐漸模糊,即使在設計時管得住自己,你也不一定能 100% 防止別人模糊你的邊界。

  4. 單個 Repo 包含了所有的源碼:出現公司源代碼泄露時,會導致整個 Repo 外泄,相當的刺激和具有教育意義。因爲雖然開放和協同了,不屬於你們組的業務代碼你也有權限查看了。

當然,Monorepo 是否又完全不可行呢?實際上國外 Google,Facebook,Twitter 等公司都有在使用 Monorepo,並取得了一定的收益。

其實做 Monorepo 是需要相應的大量工具支撐,若單純只是一個 Repo 塞多個模塊,基本都做不好,甚至引火燒身。還不如早早拆開,至少能確保各業務線服務的相對獨立性。

拆成什麼樣


張三在明白了拆的原因後,就出現了第二個問題,那就是 “微服務” 要按照什麼樣的維度去拆分服務?

張三公司內部對於這塊的知識處於模糊不清的階段,因此需要進行深入瞭解,便於後續的團隊共識和方法論建立,理所當然,十萬個爲什麼也就出現了。

大單體變獨立服務

最常見的拆分的方式是按照業務模塊進行服務的拆解,像是前文所提到的業務模塊,在設計上邊界非常清晰,這種情況直接拆成各個服務就可以了:

而在拆分後,又會遇到一個新的問題,也就是張三問第三個問題 “每個業務組拆分的維度似乎都不大一樣?”。

因爲在實際的執行過程中,嚴謹一些會由 SM 與 RD 一同開會探討 / 規範初版的服務劃分,而在持續的快速的迭代中,往往新服務的拆分都是由一線 RD 親自操刀。

即使是架構師親自操刀,在相對複雜的業務模型下,不同架構師劃分出來的也有可能不完全一致,因此無論是哪種情況,你都會發現每個業務組拆分的維度多多少少都不一樣了,畢竟人與人的思想都是不一樣的,一千個人有一個千個哈姆雷特,因此張三的疑惑是正常的。

就像下圖,核心是定義一隻魚,在不同人的眼中能演化出各種奇奇怪怪的魚:

大數據庫變獨立數據庫

在以前早期的大單體快速迭代中,往往是一個大數據庫包含所有的業務數據庫(甚至數據庫賬號都不分),這種時候就會帶來各種問題。

像是某一天,你所負責的業務模塊數據庫莫名其妙出現了一些奇奇怪怪的值,你可能就要抓破腦袋去各種代碼和 binlog 查了。更甚還有被網絡攻擊後,數據庫配置被獲取,直接跳板一拖直接整個脫褲,那可是糟糕透頂了。

因此在常見的應用設計中,應用程序在連接數據庫時會指定連接特定的域名(例如:eddycjy-user),方便未來遷移。並且每個業務服務分別給予獨立的數據庫只讀權限,進行軟隔離。

而在業務量上來後,也會對業務數據庫進行硬隔離,分配特定的 RDS 實例,就不會互相影響了。

環境隔離,獨立

在服務拆分後,大多會採取獨立部署的方式,將兩者之間的環境隔離開來,互不干擾,互不影響:

像在雲原生中,常見於在 Kubernetes 將一個業務服務作爲一個 Service 部署發佈,再根據實際的資源和調度情況進行 Pod 的擴縮容就可以了,資源也不會有直接干擾,且外部 / 內部調用都是有統一的入口管理。

拆分的陣痛

業務接口聚合


在服務拆分的過程中,總是會有陣痛出現。

例如在服務需要獲取 “項目” 和 “房源” 信息時,到底是由誰來聚合這兩個服務的信息。是不是應該由 BFF 來聚合:

或是應該新寫一個膠水服務,用於聚合 “項目” 和 “房源” 信息,保證其聚合性,減輕 BFF 的負擔:

又或是在量級越來越多的情況下,是不是要懷疑一下,這兩個服務拆分是不是有問題,“項目” 和 “房源” 在當前業務模型下是否應是一家:

顯然在鯉魚的經歷中,這三種類型他都見過,不同的人總會在不同的思想和業務模型下選擇了不同的解決方案,還真的沒有絕對準確的準則。

分久必合,合久必分

隨着對服務化的進程推進,常見的會遇到兩種情況:

大多數的情況都是第二和第三者,但在實際操作中也不見得會合並服務,大多數 RD 會選擇吞進心裏,因爲服務變遷所帶來的工期延長和影響面無法直接預估(且存在歷史代碼,人員可能已經離職多年)。即使是服務拓撲也只能查看到一定時間內的服務調用,不會看到全部,因此上下游均無法 100% 確定。因此綜合來看,弊大於利。

在解決方案上,更多的是在下次新服務規劃時控制劃分變量(因爲已經有更成熟的經驗了)。

實在不行了,纔有可能會新起聚合服務將原本的多個服務聚合,又或是採取版本號等方式進行新老分流。甚至下定決心,螞蟻搬家,起新服務一個個板塊重構,一個個挪,持續灰度,“徹底” 解決歷史包袱,完成轉化。

拆分準則


張三又發話了,你說的我都懂,內部微服務都發展好幾年了,作爲已經有豐富研發經驗的人,能不能釋出一套微服務拆分的準則呢,否則每一個人都要經歷一遍,怎麼辦,有沒有什麼基本準則可以遵守呢,你看現在 DDD 那麼火,能不能 DDD 一下,讓核心一致呢?

機智的鯉魚掐指一算,張三肯定想的是讓所有業務組的拆分,都能依據拆分的核心準則走,實現你中有我,我中有你,看哪哪都有影子,核心不跑偏就行,建立一套完美的方法核心論:

這種建議右拐 Google “微服務如何拆分”,網上有超級多的指導資料,建議先培養在團隊內的共識。畢竟在每次拆服務時讓每一個人都對照着那一長串的 “微服務拆分準則” 是一件很不科學的事情,更多的工程師會依據自身的經驗進行當前其認爲的最合理拆解。

而準則,你認爲的核心 A,在他人眼裏並不一定是正確,他可能認爲是 B,因此在事業部,業務團隊中達成共識並把拆分思想融合進每位 RD 思想中,長期的共同分析現在的拆分情況,且讓大家基本認同纔是最重要的。

同時讓全公司都依據一個準則來做,在服務拆分這種無法利用工具流程強控制的情況,本身就是一個僞命題,更多的會是人與人之間的妥協,基本上會變成一個少有人看的 “指導” 文檔。

總結

在微服務中,服務的拆分總是能讓人如此細細品味,本文並不是具體的講某幾個知識點,更多的是闡述在服務化發展的歷程中的 “衝突點” 又或是 “矛盾點”,不同的人總有不一樣的理解,希望能夠給大家帶來一些思考。

且在閱讀微服務相關指南時,更建議看企業實踐後拆分的經驗分享,否則單純看 “指南” 沒有過多的意義,要看具體的公司 / 團隊情況和業務模型。

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