微服務的戰爭:按什麼維度拆分服務

EDDYCJY發表於2020-08-25

? 點贊再看,養成習慣,微信搜一搜【腦子進煎魚了】吸取煎魚的精華 ?。最近熱衷於分享 Go 語言、微服務架構和各類奇怪的系統設計。若有任何建議或疑問歡迎隨時交流和反饋。

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

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

  1. 張三:為什麼要拆現在的程式碼?

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

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

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

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

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

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

為什麼想拆

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

image

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

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

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

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

  4. 單個 Repo 包含了所有的原始碼:出現公司原始碼洩露時,會導致整個 Repo 外洩,相當的刺激和具有教育意義。因為雖然開放和協同了,不屬於你們組的業務程式碼你也有許可權檢視了。

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

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

拆成什麼樣

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

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

大單體變獨立服務

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

image

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

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

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

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

image

大資料庫變獨立資料庫

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

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

image

因此在常見的應用設計中,應用程式在連線資料庫時會指定連線特定的域名(例如:eddycjy-user),方便未來遷移。並且每個業務服務分別給予獨立的資料庫只讀許可權,進行軟隔離。而在業務量上來後,也會對業務資料庫進行硬隔離,分配特定的 RDS 例項,就不會互相影響了。

環境隔離,獨立

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

image

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

拆分的陣痛

業務介面聚合

在服務拆分的過程中,總是會有陣痛出現。例如在服務需要獲取 “專案” 和 “房源” 資訊時,到底是由誰來聚合這兩個服務的資訊。是不是應該由 BFF 來聚合:

image

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

image

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

image

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

分久必合,合久必分

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

  • 剛接觸服務化時:服務一個沒有,偶爾會有一個新的小業務,居然能拆出好好幾個微型服務,並揚言要把剩餘業務直接抄底重構了,都拆掉,怎麼勸都勸不回頭。

  • 隨著業務的不斷髮展:快速迭代,服務越來越多,工期壓縮,多個 RD 交叉背好幾個業務服務,有點力不從心,發現拆的好像有點問題,從最新的情況來看,某某幾個服務似乎應該合在一起。

  • 業務階段性穩定:。。。這,以前這塊好像有點問題,也太難擴充了,不應該這麼拆,誰調了我,我的上下游是誰。

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

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

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

拆分準則

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

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

image

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

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

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

總結

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

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

推薦閱讀

Monorepo:

Microservices:

我的公眾號

image

更多原創文章乾貨分享,請關注公眾號
  • 微服務的戰爭:按什麼維度拆分服務
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章