好的架構不是買來的,也不是最開始就被設計出來的,而是在業務發展中,逐步演化過來的。項目剛開始,應抱着最小可用產品的理念,儘快作出最小可用產品,給客戶使用獲取反饋,而後基於反饋快速的迭代開發。在沒有交付使用前,再好的架構都是假設,產品越晚使用,失敗的風險和成本越高。程序員
在開發的初期,咱們對於服務的拆分,每每是根據產品或者是客戶的需求,經過恰當的分層和包名來完成服務的拆分。數據庫
這個也就是咱們一般所說的單一應用,在這個階段,咱們一般會有以下幾個準則:服務器
一、爲了下降耦合,系統應進行恰當的分層,好比咱們常說的MVC。架構
二、用於增刪改查的ORM框架是一個關鍵,一個好的ORM框架能大大提升咱們的開發效率。框架
三、一些基本的、通用的框架好比Spring應該被引入,爲了考慮到之後新人的加入和持續的可維護性,咱們不該該使用一些冷門的框架。分佈式
四、咱們會從需求中抽象出來一些關鍵名詞、動詞和屬性,並對一些核心概念進行抽象,完成建模,並在團隊內造成「通用語言」,咱們在團隊的平常交流中都應該使用這些「通用語言」。工具
五、咱們應該有數據庫和持續集成服務器,便於快速發佈應用,必要時要引入代碼審查工具。開發工具
在這個開發的過程當中,一切都有條不紊的進行,產品經理的需求能夠很快獲得了知足,更多的新同事加入進來,更多的功能也加入進來,全部人都對現狀和架構很滿意,由於這個單一的項目架構是如此的清晰、簡單、高效。團隊不大,交流也很是高效,你們配合的很默契。設計
但是好日子不長,產品經理有不少的想法,新的功能一個個被添加進去,代碼變的很是臃腫,每次編譯都須要花費很長時間。這時開始有人想到重構,在重構的過程當中,會把一些通用的代碼封裝成common項目,也可能把不一樣的層,好比service層獨立封裝成一個項目。日誌
但是隨着新加入的員工愈來愈多,並非全部人都對全部的代碼都很熟悉,構建時間愈來愈長,開發工具也慢了下來,代碼變的愈來愈臃腫。開發速度也慢了下來,每次想增長一個新的功能,會發現代碼之間縱橫交錯,不知道從哪裏下手,爲了快速實現需求, 無奈只能新增長一個接口,可能他並不知道已經有相似的接口存在,慢慢的增長了不少冗餘代碼。忽然有一個程序員,想把某一個功能重構,可是他翻看一下代碼,發現有不少看不懂的業務,無奈他放棄了。隨着愈來愈多的相似事情發生,最終沒有一我的能清楚的知道系統是怎麼運行的。
此時此刻,系統一般會面臨以下幾個問題:
一、開發速度慢,編譯速度慢,代碼之間耦合嚴重,每次開發一個新功能,均可能會牽涉到不少模塊,在開發的過程當中有不少隱患很難排查。
二、項目過大,發版困難,在發版時,甚至可能須要停機。
三、業務複雜,新人接手困難,任何人都不敢輕易調整代碼,隨便調整一處,均可能影響到不少本身不知道的模塊。
四、技術升級困難,團隊對於新技術愈來愈保守,不敢隨意引入新技術,會增長技術風險。
五、鏈接池之類的底層資源壓力巨大,由於數據庫是單點,在機器的橫向擴展過程當中,數據庫鏈接數過大。
假如你沒有知足上述條件,博主並不建議去服務化,由於服務化有不少之前單點系統中沒有碰見過的挑戰,須要提早規劃起來,好比通信、分佈式事務、服務如何拆分以及拆分的粒度等問題。
如何進行服務拆分和重構
從我最近接手的項目爲例。
這個項目的現狀很是棘手,團隊幾乎都是新人,幾乎沒有一我的對系統是如何運行有清晰的認識,項目時間過長,開發過程當中沒有文檔,只能依靠代碼中零星的註釋。數據庫表有近百張,至於真正用到了哪些表,沒有一我的能說的清除。和外部系統之間調用模糊,不清楚此係統和外部系統是如何交互的。
在進行拆分和重構以前,咱們一般要作以下幾件事:
一、須要先摸透全部的業務和數據庫。
二、到底有哪些真實在使用的接口,能夠依靠訪問日誌進行排查。
三、和以前參與過的同事進行充分的瞭解和溝通。
四、排查清楚和外部系統的關係,明確服務的職責和邊界,在此過程當中須要結合業務進行劃分。這個過程還須要協調對應的項目組,進行配合整改,外部協調可能會帶來不肯定性,須要考慮這些服務是否要先上線。
五、肯定數據遷移方案,和產品進行溝通,肯定必需要遷移的數據,可能有一些數據並不須要遷移到新系統,或者能夠經過其餘方式進行解決。
拆分的目標
在重構時,咱們應該將系統中的獨立業務模塊抽取出來,按照業務的獨立性進行垂直劃分,抽象出基礎服務層,抽象的目標是,每個基礎服務層都是無狀態的和去中心化的,能夠彈性部署,可隨着系統的負荷靈活伸縮來提供服務能力,這些基礎服務都是經過底層中心繫統發生關係的,好比配置中心、日誌服務、消息服務、柔性事務服務等。這些基礎服務會爲上游業務系統提供支撐。
在進行服務化拆分時,咱們還須要考慮到的一個問題就是,組織溝通方式決定系統設計,這個主流的理論就是康威定律。咱們指望拆分後的服務能夠減小協同、減小環節、提高效率。說的通俗一點就是,若是某個應用,須要多個組織之間一塊兒交流和修改,那麼它的交流成本就大於組織機構了,出現了不匹配的狀況,那麼這個應用就極可能太粗而須要拆分。
咱們能夠將業務的流轉過程畫成圖,先找出其中的名詞和動詞,每一個名詞和動詞均可能是一個服務。
在服務數量上的劃分時,並無一個準則。服務過多,可能會致使劃分過細,破壞業務系統之間的獨立性,維護工做量變大,部署時佔用內存多,並且定位問題困哪重重,還很難梳理服務之間的依賴關係,好比支付訂單、退款訂單,被劃分紅了兩個服務。服務過少,可能沒有實現很好的解耦,開發維護很差分工,升級影響面變大,好比一個服務要升級,會影響不少業務不可用。
通過服務拆分後,每一個服務一般都有本身獨立的數據庫,在數據庫查詢時,不要出現A服務中的SQL須要連接查詢到B服務中的表等狀況,這樣在A服務與B服務進行數據的垂直拆庫時就會出錯。
在服務調用時,服務子系統間應該避免出現環狀的依賴調用(好比A服務依賴B服務,B服務依賴C服務,C服務依賴A服務)。
在服務依賴上,服務子系統間的依賴關係鏈不要過長(不能超過3個)。
在拆分時,應該儘可能避免分佈式事務,除非能夠解決分佈式事務,不然沒法達到一致性。
從經驗上來講,初期應該根據業務劃的比較大塊,好比帳戶模塊和用戶模塊合成一個服務,交易和訂單做爲一個服務,大概分到3至5個服務,根據業務的發展狀況,須要細分時再去分。
服務聚合
在拆分後,每一個服務都只對本身的業務負責,這些服務對外提供的接口粒度可能都比較細。但總會有一些複雜業務須要依賴多個服務才能實現業務需求。對於這個問題,以我這次重構的經驗來講,我會對每個業務系統(好比某一個App),都建一個對應的後臺服務,複雜的需求、須要多個服務組合完成的需求會經過後臺服務進行訪問,依靠這個App Server完成服務聚合。