常見的中型服務架構
目前來看,工程的部署方式還是采用一體化架構,也就是說所有的功能模塊,比方說電商系統中的訂單模塊、用戶模塊、支付模塊、物流模塊等等,都被打包到一個大的Web工程中,然后部署在應用服務器上。
一體化架構的痛點
先來回想一下,當初為什么選用了一體化架構。
在電商項目剛剛啟動的時候,只是希望能夠盡量快地將項目搭建起來,方便將產品更早地投放市場,快速完成驗證。
在系統開發的初期,這種架構確實給你的開發運維,帶來了很大的便捷,主要體現在:
- 開發簡單直接,代碼和項目集中式管理;
- 只需要維護一個工程,節省維護系統運行的人力成本;
- 排查問題的時候,只需要排查這個應用進程就可以了,目標性強。
但隨着功能越來越復雜,開發團隊規模越來越大,你慢慢感受到了一體化架構的一些缺陷,這主要體現在以下幾個方面。
第一點,在技術層面上,數據庫連接數可能成為系統的瓶頸。
數據庫的連接是比較重的一類資源,不僅連接過程比較耗時,而且連接MySQL的客戶端數量有限制,最多可以設置為16384(在實際的項目中,可以依據實際業務來調整)。
這個數字看着很大,但是因為你的系統是按照一體化架構部署的,在部署結構上沒有分層,應用服務器直接連接數據庫,那么當前端請求量增加,部署的應用服務器擴容,數據庫的連接數也會大增,給你舉個例子。
不僅要支撐來自客戶端的外網流量,還要部署單獨的應用服務,支撐來自其它部門的內網調用,也要部署隊列處理機,處理來自消息隊列的消息,這些服務也都是與數據庫直接連接的,林林總總加起來,在高峰期的時候,數據庫的連接數也不小。
所以,一旦遇到一些大的運營推廣活動,服務器就要擴容,數據庫連接數也隨之增加,基本上就會處在最大連接數的邊緣。這就像一顆定時炸彈,隨時都會影響服務的穩定。
第二點,一體化架構增加了研發的成本,抑制了研發效率的提升。
《人月神話》中曾經提到:一個團隊內部溝通成本,和人員數量n有關,約等於n(n-1)/2,也就是說隨着團隊人員的增加,溝通的成本呈指數級增長,一個100人的團隊,需要溝通的渠道大概是100(100-1)/2 = 4950。那么為了減少溝通成本,我們一般會把團隊拆分成若干個小團隊,每個小團隊5~7人,負責一部分功能模塊的開發和維護。
比方說,垂直電商系統團隊就會被拆分為用戶組、訂單組、支付組、商品組等等。當如此多的小團隊共同維護一套代碼,和一個系統時,在配合時就會出現問題。
不同的團隊之間溝通少,假如一個團隊需要一個發送短信的功能,那么有的研發同學會認為最快的方式,不是詢問其他團隊是否有現成的,而是自己寫一套,但是這種想法是不合適的,這樣一來就會造成功能服務的重復開發。
由於代碼部署在一起,每個人都向同一個代碼庫提交代碼,代碼沖突無法避免;同時,功能之間耦合嚴重,可能只是更改了很小的邏輯,卻導致其它功能不可用,從而在測試時需要對整體功能回歸,延長了交付時間。
模塊之間互相依賴,一個小團隊中的成員犯了一個錯誤,就可能會影響到,其它團隊維護的服務,對於整體系統穩定性影響很大。
第三點,一體化架構對於系統的運維也會有很大的影響。
想象一下,在項目初期,代碼可能只有幾千行,構建一次只需要一分鍾,那可以很敏捷靈活地頻繁上線變更修復問題。但是當你的系統擴充到幾十萬行,甚至上百萬行代碼的時候,一次構建的過程,包括編譯、單元測試、打包和上傳到正式環境,花費的時間可能達到十幾分鍾,並且,任何小的修改,都需要構建整個項目,上線變更的過程非常不靈活。
而這些問題,都可以通過微服務化拆分來解決。
如何使用微服務化解決這些痛點
項目的一體化的架構,數據庫已經做了垂直分庫,分出了用戶庫、內容庫和互動庫,並且已經將工程拆分了業務池,拆分成了用戶池、內容池和互動池。當前端的請求量越來越大時,無論哪個業務池子,用戶模塊都是請求量最大的模塊兒,用戶庫也是請求量最大的數據庫。這很好理解,無論是內容還是互動,都會查詢用戶庫獲取用戶數據,所以,即使做了業務池的拆分,但實際上,每一個業務池子都需要連接用戶庫,並且請求量都很大,這就造成了用戶庫的連接數比其它都要多一些,容易成為系統的瓶頸。
怎么解決這個問題呢?
其實,可以把與用戶相關的邏輯,部署成一個單獨的服務,其它無論是用戶池、內容池還是互動池,都連接這個服務來獲取和更改用戶信息,那么也就是說,只有這個服務可以連接用戶庫,其它的業務池都不直連用戶庫獲取數據。
由於這個服務只處理和用戶相關的邏輯,所以,不需要部署太多的實例就可以承擔流量,這樣就可以有效地控制用戶庫的連接數,提升了系統的可擴展性。那么如此一來,也可以將內容和互動相關的邏輯,都獨立出來,形成內容服務和互動服務,這樣,我們就通過按照業務做橫向拆分的方式,解決了數據庫層面的擴展性問題。
再比如,做社區業務的時候,會有多個模塊需要使用地理位置服務,將IP信息或者經緯度信息,轉換為城市信息。比如,推薦內容的時候,可以結合用戶的城市信息,做附近內容的推薦;展示內容信息的時候,也需要展示城市信息等等。
那么,如果每一個模塊都實現這么一套邏輯就會導致代碼不夠重用。因此可以把將IP信息或者經緯度信息,轉換為城市信息,包裝成單獨的服務供其它模塊調用,也就是可以將與業務無關的公用服務抽取出來,下沉成單獨的服務。
按照以上兩種拆分方式將系統拆分之后,每一個服務的功能內聚,維護人員職責明確,增加了新的功能只需要測試自己的服務就可以了,而一旦服務出了問題,也可以通過服務熔斷、降級的方式減少對於其他服務的影響。
另外,由於每個服務都只是原有系統的子集,代碼行數相比原有系統要小很多,構建速度上也會有比較大的提升。
解決的痛點
- 系統中,使用的資源出現擴展性問題,尤其是數據庫的連接數出現瓶頸;
- 大團隊共同維護一套代碼,帶來研發效率的降低,和研發成本的提升;
- 系統部署成本越來越高。