服務拆分的設計和思考(B2B 技術共享第九篇)

宋小菜技術發表於2018-06-09

本文主要想給大家分享一下,宋小菜這三年來,是如何從單點巨石系統演變成領域驅動的服務化設計的。這個演變現在還在繼續,我們在實踐過程中遇到了很多坑,也收穫了經驗和思考。

一、早期的系統

1.1 唯快不破

我是在宋小菜處於籌備階段就加入的第一個程式設計師,見證了宋小菜系統從0到1的全過程。那時候的技術團隊只有一個產品經理,一個程式設計師(就是我),還有五個外包同學。在宋小菜第一天工作的日子我還記得非常清楚,是2015年1月18日,我們的第一個任務我也記得非常清楚,在3月份前產出一個可以交易的平臺。交易需要什麼,使用者、賬戶、商品、資金、訂單……這些模組組成了宋小菜最早的交易系統。那時候為了快,犧牲了太多的東西,比如缺乏了整體的架構設計、缺乏了充足的業務理解、缺乏了功能的抽象、缺乏了效能的分析。這些行為在現在看起來,非常不可思議也不可接受。我時常在想一個問題,如果以我們現在的經驗和理解,穿越回到三年前去開發最早期的宋小菜系統,又會是一番怎麼樣的場景呢?我會一開始就規劃出使用者中心、商品中心等幾大中心嗎?我會一開始就設計無狀態系統和水平擴充套件能力嗎?我會一開始就開發閘道器平臺收攏和規範所有API的呼叫嗎?答案一定是不會的,而且這樣做結果往往也不一定會比當初更好。 早期系統並不需要一些類似核武器一樣的架構設計和中介軟體,真正需要的是最快速度為公司的業務提供戰鬥的能力。

1.2 面臨的問題

那時候主要的系統有兩個,分別為內部的ERP系統和供客戶端呼叫的API服務系統,這兩個系統完成了我們的交易的閉環。隨後的兩年時間裡,瘋狂的業務程式碼堆積和不受控制的重複程式碼,使得這個系統越來越笨重。那時候的系統結構非常簡單,公共的模組以jar包的形式抽離出來,其中ERP系統的依賴關係可以參見下圖:

image1.png | center | 1338x1274

這時候的系統改造就變得非常痛苦,每次釋出都會變得異常緊張,生怕有哪個模組的jar包改動了沒有更新,就導致系統無法順利啟動。曾經有那麼一段時間,我會很懼怕系統的釋出,如果順利釋出,我也會長舒一口氣。
複製程式碼
再發展下去,就已經不是害怕系統釋出這麼簡單了,系統逐漸進入一個可怕的死迴圈了:
服務拆分的設計和思考(B2B 技術共享第九篇)
在系統的各個地方散落了一些異常刺眼的註釋,比如“這段程式碼太牛逼了,我不敢改,所以就複製了一份”、“這部分的邏輯是xxx寫的,不要輕易改動”等等。以下六點是我們系統存在的主要問題:
複製程式碼
  1. jar包管理混亂,經常導致系統無法構建;
  2. 笨拙、耦合嚴重,釋出影響點混亂,底層jar的變更,導致每個應用系統都需要釋出;
  3. 開發過程中協作困難;
  4. 新人很難上手,理解困難;
  5. 不容易管理,出現很多不潔程式碼;
  6. 技術債務越背越多; 當公司規模還小的時候,業務複雜度和系統複雜度都還處於一個比較好接受的階段,巨石系統往往是一個比較高效的架構設計。但是隨著業務的不斷擴充套件,複雜度和訪問量進一步的增加,如果突破了某一個臨界點了,系統架構的升級和服務的拆分,就變成不得不做的一件事了。

二、服務拆分上遇到的坑

2.1 缺乏規劃

當時決定進行服務拆分的時候,技術團隊的規模也並不是特別龐大,也並沒有哪位同學對於服務的拆分有較多經驗。現在回想起來,那時候的起步確實草率了點。我們沒有做太多的規劃,直接到了“說幹就幹”的環節了。可想而知服務會出現的多麼凌亂,一會一個“價格中心”,一會一個“許可權中心”,哪個簡單就先拆哪個,哪個新業務要上了,先起一個服務再說。隨後就暴露出了伺服器資源不夠、運維困難、服務之間呼叫混亂等問題。 大家意識到這個問題的時候,還不算病入膏肓。我們立刻進行了宋小菜整體服務大盤的設計和劃分,將服務分為上下兩層。上層為業務層,下層為能力層,儘量使得服務之間的呼叫變成樹狀,而不是環狀。

image3.png | center | 2198x1154

2.2 服務的粒度

服務的拆分是一件非常容易的事情,關鍵在於粒度的把控。那時候對於微服務有一種近乎偏執的理解,認為只要某個功能模組獨立,就得變成一個服務獨立出來,而且為了保證可用性還必須起兩個節點。按照這個理論我們進行了實踐,也確實出現了不少極端的操作。逐漸我們明白這個思路是有一些問題的,而要解決這個問題,我們必須對於服務拆分的理念達成統一。 我們嘗試使用DDD(領域驅動設計)來進行服務的規劃,基於DDD的設計方法,從當前業務出發,抽象業務找到宋小菜業務的核心能力,並以此作為服務進行拆分。但DDD是方法論,並不是教條和金科玉律,在使用的過程中也千萬不要走火入魔。 使用了DDD進行分析之後,我們發現了一個更加頭疼的問題,那就是以前的服務拆的太細了,把很多不該拆分出去的服務拆成了兩個甚至更多。所以之後我們內部啟動了“方舟專案”,該項的目的主要有這些:

  1. 對於服務如何拆分,在團隊中構建共同的認知,並推進使用DDD;
  2. 定義微服務工程結構和規範;
  3. 將以往粒度過細的服務進行合併; 通過這次趟坑,我們也明白服務的拆分是一件非常慎重的事情,拆分一時爽,要是再想合併和統一,就不是那麼容易的一件事情了。

2.3 可能出現的風險

2.3.1 經驗

開發團隊是否具備足夠的經驗,能否駕馭微服務的技術棧,可能是第一個需要考慮的點。這裡並不是要求團隊必須具備完善的經驗才能啟動服務拆分,如果團隊中有這方面的專家固然是最好的。如果沒有,那可能就需要事先進行充分的技術論證和預演,至少不打無準備之仗。 上文也提到了,我們的啟動略微草率了一些,團隊中也並沒有這方面非常專業的同學坐鎮,所以在一些分散式常見的問題上,都踩了坑,比如呼叫重試、超時機制、分散式事務等,這些問題一出現,很多沒有經驗的同學會非常抓狂,甚至無從下手。

2.3.2 穩定的測試

服務的拆分,必然會出現的問題就是加大了同學們的開發自測難度。以前的系統無論是在本地啟動,或是釋出在測試環境,所有的呼叫都是確定性的。但是一旦某一塊服務拆分出來了,可能會面臨很多問題:

  1. 本地針對遠端服務的呼叫,可能是被禁止的,那就需要使用mock的方式來解決了,在本機單元測試的時候,主要測試的是業務邏輯和流程,並不能很完美的測到遠端呼叫的返回資料。
  2. 呼叫的測試環境可能很不穩定,當你想要呼叫的時候,服務可能早就掛了。
  3. 多個專案組開發新功能的時候,呼叫了同一個服務,這時候有可能會因為伺服器資源有限,導致大家資源競爭。 不同公司情況不同,為開發同學營造的測試環境也不盡相同。但有一點是一定的,如果測試複雜,會給開發同學造成很大的壓力。明明一個很簡單的功能,改一改可能只需要5分鐘,但是想走完一次完整的測試,卻花了他1個小時的時間。如果在服務化拆分後不能很好的解決,會導致開發同學有越來越多的僥倖心理和偷懶的情況,不進行測試就提交了程式碼或進行了釋出。

2.3.3 日誌

服務的拆分必然會導致日誌散落在各地,無論是定位問題需要檢視日誌,還是一些事件需要基於日誌去實現,都會變得比以往復雜很多。一開始的時候,我們並沒有意識到這個問題,所以很多開發同學出現bug或是故障之後,不知道如何去定位了。因為一個方法的呼叫,可能會因為服務化的呼叫被放大幾次甚至十幾次,最後的錯誤到底出現在哪裡,如何去找去看,都是一個問題。

三、結束語

服務拆分可能出現的問題還有很多,在這裡也只是將一些最容易出現的場景分享給大家。希望通過本文,可以給那些準備進行服務拆分,或在正處於服務拆分起步階段的團隊一些建議和經驗。

關於如何搭建高效率的生鮮B2B平臺,因為包含的內容較多,也很複雜,無法再一篇文章中給大家講清楚,本篇文章只是拋磚引玉,下面將分為多篇文章從行業現狀、業務現狀、產品概述、技術團隊搭建、服務端技術平臺搭建、前端開發等多個維度來講述,我們將三年多在B2B領域沉澱的核心產品和技術平臺公開,希望更多行業的人能深入瞭解,少走一些彎路,希望對大家有幫助,本系列文章分佈如下(會繼續更新):

1、《如何搭建高效率的生鮮 B2B 平臺(B2B 技術共享第一篇)》

2、《宋小菜如何切入生鮮 B2B 市場(B2B 技術共享第二篇)》

3、《生鮮 B2B 平臺的產品體系如何迭代(B2B 技術共享第三篇)》

4、《生鮮 B2B 如何搭建高效的技術團隊(B2B 技術共享第四篇)》

5、《如何從 0 到 1 搭建生鮮 B2B 的技術體系(B2B 技術共享第五篇)》

6、《宋小菜技術如何應對生鮮 B2B 業務的快速變化(B2B 技術共享第六篇)》

7、《生鮮 B2B 技術平臺的前端團隊該如何搭建(B2B 技術共享第七篇)》

8、《宋小菜有關“能力”的設計和思考(B2B 技術共享第八篇)》

9、《服務拆分的設計和思考(B2B 技術共享第九篇)》

相關文章