- 相關推薦
微服務的架構設計
近年來,服務化和微服務的架構隨著線上業(yè)務對響應變化和發(fā)布頻率要求的不斷提高已經(jīng)變得日益常見。下面yjbys小編為大家準備了關于微服務架構設計的文章,歡迎閱讀。
前言
許多企業(yè)對微服務思考的關注點也從最初的“該不該用”、“該如何用”逐漸轉向“如何評價服務拆分的好壞”、“服務拆分后的數(shù)據(jù)如何治理”這樣更加具體而實際的方面?v觀社區(qū)里與微服務有關的話題,既有一些適合大眾閱讀的概念入門科普文章,也有一些像“架構去中心化”、“消息異步解耦”、“最終一致性補償”這類專業(yè)性的技術討論,但卻很少看到能夠比較深入的介紹微服務實施經(jīng)驗,說清楚“什么地方會有什么坑”這類問題的實踐性內容。
若將微服務的方方面面鋪開,將是一個非常龐大而復雜的知識體系。萬事開頭難,如何邁出服務劃分的第一步是對于那些從未采用過微服務的項目首先要面對的問題。雖然服務的劃分本來是一件十分憑借架構師個人經(jīng)驗和對業(yè)務理解的主觀工作,但其中仍然有些規(guī)律可循。根據(jù)服務的類型特點,可以有以下幾種常見的方式:
1. 根據(jù)業(yè)務進行建模,依據(jù)業(yè)務領域的邊界劃分,這也是當下微服務社區(qū)十分推崇的領域驅動設計(Domain Driven Design,簡稱DDD)方法;
2. 根據(jù)資源使用類型劃分,這種方式主要使用在對硬件資源需求很高、或對特定硬件類型/區(qū)域地址等存在依賴的大型服務系統(tǒng),例如系統(tǒng)的某些功能非常消耗CPU、或是某些功能必須在有加密狗設備的機器上運行。此時可以依據(jù)系統(tǒng)各部分對不同資源的需要作為邊界進行最安全的劃分,以最大化運行效率;
3. 根據(jù)數(shù)據(jù)邊界劃分,直接從數(shù)據(jù)表結構或數(shù)據(jù)源著手,依據(jù)數(shù)據(jù)歸屬關系界定服務。這種劃分方法簡單粗暴且能避免數(shù)據(jù)耦合,但容易導致潛在隱患,甚至可以被認為是一種反模式。僅僅對于強數(shù)據(jù)驅動的系統(tǒng),如某些報表系統(tǒng)和多數(shù)據(jù)源ETL系統(tǒng)等適用,在實際案例中很少見。
從現(xiàn)實上來說,我們平時遇到的絕大部分系統(tǒng)都是需要為特定終端用戶群體服務的。因此,采用基于業(yè)務領域的建模的方法通常都會是個不錯的選擇,它也是目前在劃分服務問題中的最主流的方法。不過即使有了理論指導的支撐,服務劃分里的利弊權衡并不是非黑即白的事情,依然存在不少模棱兩可之處,只有在犯下一些錯誤后才能摸索出適合自己系統(tǒng)的方法。本文將會聚焦在這些點。
沒有什么能比一個具體而貼近真實的案例更具有代表性了。因此,我們將剖析一個傳統(tǒng)行業(yè)企業(yè)的線上業(yè)務:某汽車產(chǎn)品代理商的線上銷售和售后平臺,介紹它在微服務轉型過程中遇到的種種情況,希望能以此作為前車之鑒,讓后來者避開不必要的趟坑。這個案例中講述的場景原型并非完全來自同一個項目,而是從我們過去一年中所經(jīng)歷的多個項目的實際場景抽取出來的,去除了其中的敏感信息,并添加了基于真實情況的適當演繹,使之更加完整。簡單介紹一下案例的背景:這是一家頗具規(guī)模的汽車代理商企業(yè),承接多個國際一線品牌汽車的銷售和周邊資源整合的業(yè)務,具有龐大的實體店網(wǎng)點。其線上的IT業(yè)務系統(tǒng)已經(jīng)存在了十余年,提供的功能從最初的進銷存管理、客戶信息管理等到現(xiàn)在的面向消費者客戶的服務系統(tǒng),十分復雜。系統(tǒng)中的線上銷售和售后平臺部分是相對比較新、且需求變化特別迅速的部分,也是目前出問題最頻繁、收到抱怨最多的部分。
不要從數(shù)據(jù)庫開始建模
傳統(tǒng)軟件開發(fā)中,數(shù)據(jù)模型被認為是整個系統(tǒng)的核心,業(yè)務邏輯僅僅是對數(shù)據(jù)的CRUD加上簡單的計算呈現(xiàn)。有些項目團隊的架構評審會花很多時間來討論系統(tǒng)龐大的ER圖(實體關系圖)設計。但在微服務架構設計時,ER圖并非最佳選擇,特別是在服務劃分時采用ER圖進行建模甚至是十分有害的。
在領域驅動設計的實踐中,有一個和ER圖建模有些相似的環(huán)節(jié),叫做“領域建模”。相比ER圖建模通常只有開發(fā)人員參與,并從數(shù)據(jù)表的角度考慮模型的方法,領域建模的過程需要由產(chǎn)品的業(yè)務人員和核心開發(fā)人員共同參與,先梳理用戶場景,然后從業(yè)務領域角度,逐步確定場景中的實體、關聯(lián)和聚合等元素。其中的“實體、關聯(lián)”與ER圖中的“實體、關系”有些相似,但含義并不一致。領域模型中的“實體”本質上是業(yè)務場景中需要被持久化存儲的對象,但存儲方式不一定是數(shù)據(jù)庫,更不一定是關系型數(shù)據(jù)庫,而ER圖中的“實體”最終對應的就是關系型數(shù)據(jù)庫的表。領域模型中的“關聯(lián)”是兩個實體在業(yè)務上下文之間有業(yè)務含義的聯(lián)系,而ER圖中的“關系”只的是兩張表之間的一個關聯(lián)外鍵。
那么,使用ER圖給微服務建模會存在什么問題呢?
首先,ER圖設計時假定了使用的是關系型的數(shù)據(jù)庫。微服務的一個特點在于它支持異構架構,系統(tǒng)的不同部分,依據(jù)實際需要可選擇不同的編程語言、框架以及數(shù)據(jù)庫的類型。關系型數(shù)據(jù)庫采用平面表的結構,如果有兩類嵌套關系的對象,只能使用關聯(lián)表來表達兩個實體之間的關系,然后通過復雜的SQL語句在查詢時將多個表拼成更大的平面表,最后在業(yè)務代碼里再分解到各個獨立的對象里去。這些單獨的表又可能在其他地方與另一個表存在關聯(lián)查詢。這樣設計出來的表結構的冗余很低,且使用非常靈活,但正是這種靈活性往導致系統(tǒng)中多個業(yè)務邏輯表出現(xiàn)錯中纏繞的關系,從而使剝離單獨服務進行異構設計變得困難。
其次,ER圖還會導致實體相似的不同業(yè)務邏輯在設計時被耦合在一起。舉一個具體的例子,在汽車代理銷售系統(tǒng)中,不同品牌汽車的購買流程后端實際上分別對接的是完全不同的分銷渠道系統(tǒng)和邏輯流程,但它們在用戶視圖上所需要的信息比較一致,因此存儲的數(shù)據(jù)結構也比較相似。這個系統(tǒng)在最初設計時采用了ER圖的方式建模,由于ER圖模型不關心業(yè)務層面上的東西,不同品牌汽車的實體數(shù)據(jù)看上去都是一種類型的數(shù)據(jù),僅僅是一個品牌字段的差異而已,因此被理所當然的設計成了同一張表。上層邏輯實現(xiàn)的時候使用了大量的if-else語句來區(qū)分各種品牌的購買流程,結果使得多條完全不同的業(yè)務線糅合在同一個上下文里,后來的開發(fā)非常容易在這里錯改、漏改代碼。若當初使用的是領域建模,則不同的購車流程會自然的被劃分到各種不同的用戶場景,即使它們在數(shù)據(jù)結構上看起來基本相同,也會被識別為兩個獨立的上下文,這就會使得未來劃分服務時能夠更加容易。
最后,ER圖設計的架構會使得系統(tǒng)的模塊之間傾向于使用數(shù)據(jù)庫集成,而非API集成。在ER圖中沒有明確劃分模塊和表的所有權關系,所有的數(shù)據(jù)表對所有的模塊都是可見的,倘若不加額外約束,各個模塊便都會輕易的讀寫其中的內容。數(shù)據(jù)集成并不會直接導致服務無法拆分,但由于數(shù)據(jù)的所有權不清晰,十分容易引發(fā)的意想不到的狀況。還是舉銷售平臺的例子,在ER圖建模得到的模型中,有一張與購買記錄相關的表。在一次銷售業(yè)務的代碼更新中,對購買結果的增加了字段,在測試過程中沒有發(fā)現(xiàn)問題,結果上線幾天以后,由于售后服務也在修改這個表而導致出現(xiàn)了臟數(shù)據(jù),造成難以排查的故障。
當然,我們并非要完全否認ER圖建模的價值,只不過通過數(shù)據(jù)庫角度建立模型的過程容易傾向于設計出龐大的單體應用,因而不太適應于服務劃分的目的。
端到端的劃分服務
在拆服務時要端到端的劃分,這是我們在設計微服務時經(jīng)常聽到的一句話。端到端的劃分,指的是一個服務負責一個業(yè)務領域,這個功能領域的所有邏輯、數(shù)據(jù)都歸它管,這有利于微服務的數(shù)據(jù)治理。與之相對的是MVC那樣的橫向服務劃分,將所有數(shù)據(jù)歸一塊,所有邏輯歸另一塊,特別是在跨團隊管理的情況,橫向劃分服務會帶來十分高昂的協(xié)調和聯(lián)調成本。
道理不必多說,還是講個例子吧。在汽車銷售平臺的最初架構中,使用了典型的MVC三層結構,由于人比較多,分成了前臺組、后臺組,就時不時要出現(xiàn)新開發(fā)一個功能,一動底層數(shù)據(jù)結構,結果上層的另一個不相干頁面掛了,一查發(fā)現(xiàn)原來那這個頁面間接的用了同一個數(shù)據(jù)模型。比較典型的例子是,有一回負責后臺的小組調整了汽車銷售服務里面的汽車參數(shù)信息相關的對象結構,結果一上線,銷售功能正常,試駕服務的頁面掛了。原來是試駕功能的前端開發(fā)人員在處理汽車信息時候,看到銷售模塊有現(xiàn)成功能,就直接拿來復用了。這便是服務上下層分團隊開發(fā)導致的問題。
此外,橫向劃分服務也不利于系統(tǒng)的開發(fā)效率的提升。不同業(yè)務服務對功能發(fā)布頻率的的需求是不一樣的,比如試駕平臺經(jīng)常推出新的優(yōu)惠促銷活動,需要盡快進行一次版本更新,于此同時售后服務有一個需要和第三方聯(lián)調的功能也已經(jīng)差不多完成并提交到代碼倉庫了,但由于需要協(xié)調第三方系統(tǒng)的時間,最近還不能夠上線,此時兩邊的業(yè)務主管就要開掐了。類似這樣的情況其實經(jīng)常發(fā)生,通常使用特性分支、特性開關等流程或者技術手段能夠規(guī)避一部分業(yè)務開發(fā)進度不一致的風險,但若要從根本上解決這種問題,還是需要端到端的按業(yè)務來劃分服務。
最后,橫向劃分模塊對于問題的追蹤調試也不友好,幾乎每次事故調查總是要穿插涉及在幾個團隊之間不停的協(xié)調開會,因為一個完整業(yè)務流總是要貫穿前后幾個層的功能。
需要指出的是,端到端劃分服務并非是說服務與服務之間都是平級的。實際上,服務之間可以再聚合成更高層級的組合服務的,以及在最頂端的API Gateway也可以算是一類服務。只不過,在核心業(yè)務層的這些服務,每個都單獨提供了某項特定的業(yè)務價值。
識別核心業(yè)務服務
微服務的架構通常并非是一開始就重頭設計出來的,而是先有整塊的單體架構,隨著業(yè)務的復雜度上升,才逐步拆分出來。業(yè)務領域建模除了能用來指導適當?shù)姆⻊談澐,另一個重要的作用是讓工作聚焦到核心的業(yè)務服務中。
無論多復雜系統(tǒng)都是為特定業(yè)務價值而存在的。在系統(tǒng)的實現(xiàn)中必然會存在與核心業(yè)務最相關的部分、輔助核心業(yè)務的部分、和非核心業(yè)務關系的部分。它們在領域驅動設計的術語中稱為“核心域”、“支撐子域”和“通用子域”。在進行領域建模的時候就應該順便識別出系統(tǒng)里的關鍵領域。將系統(tǒng)的業(yè)務領域羅列出來然后劃分出重要性,這件事情聽起來似乎是多此一舉,甚至有點荒唐,但對于復雜系統(tǒng),實際去做這件事帶來的價值可能遠比它看起來更大。
首先,當我們將一個復雜的系統(tǒng)的各種業(yè)務仔細清點到臺面上以后,得到的列表往往會比許多人最初想象得長得多,它能提醒我們系統(tǒng)的復雜度是否已經(jīng)過高了。其次,列舉業(yè)務的過程也是開發(fā)者與業(yè)務人員溝通的過程,來自不同業(yè)務線的代表也許會為某部分業(yè)務的價值點發(fā)生爭執(zhí),或是提供一些許多開發(fā)人員此前并不了解的細節(jié)信息,將這些問題當面討論清楚并非什么壞事。此外,當我們真的去仔細思考一個業(yè)務系統(tǒng)的核心價值時,也許會得出使人意想不到的結果。
繼續(xù)舉例子,在汽車的銷售和售后平臺中,通過業(yè)務建模,可以劃分出許多子領域:
• 在線購車(渠道A)
• 在線購車(渠道B)
• 在線購車(渠道C)
• 試駕預約
• 售后服務
• 用戶反饋
• 訂單系統(tǒng)
• 促銷活動
• 車輛市價信息
• 車輛參數(shù)信息
• 4s店信息
• 用戶信息管理
• …
說明:這里渠道指的是銷售平臺對接的一個第三方系統(tǒng),其中每個渠道可以對應多個汽車品牌
然后我們要從中識別出業(yè)務中的核心域。必須強調,服務領域劃分僅僅是代表服務對系統(tǒng)關鍵業(yè)務貢獻的價值,處于核心域中的那些服務應該是該系統(tǒng)業(yè)務成功的主要促成因素,從戰(zhàn)略層面講,企業(yè)應該在自己的核心領域上具有一定壁壘優(yōu)勢。經(jīng)過討論,開發(fā)者和業(yè)務人員最終得出讓人大跌眼鏡的結論,核心域部分的服務只有“試駕預約”和“售后服務”,因為這兩項才是該企業(yè)最具競爭力的業(yè)務。而看起來十分重要的“在線購車”服務,由于并不具有特別的行業(yè)競爭優(yōu)勢而被劃到了支撐子域中。正確的服務劃分定位將對系統(tǒng)未來的發(fā)展策略產(chǎn)生積極的影響。
在這個系統(tǒng)中的“車輛市價信息”、“車輛參數(shù)信息”、“4s店信息”等服務都被劃歸到了通用子域。在通用子域中的服務并非最沒有價值或是復雜度最低,而只是說明系統(tǒng)在這些服務領域中通常不具優(yōu)勢,因此這部分功能完全可以考慮外包開發(fā)或者購買第三方的現(xiàn)成服務。
使用符合業(yè)務結構的API
前面介紹業(yè)務建模的時候我們強調了使用“API集成”的必要性,以及它的反面形式“數(shù)據(jù)庫集成”所帶來的問題。在微服務的架構中,服務的技術選型可能是異構的,API的實現(xiàn)也會各有不同。除了Web應用比較流行的RESTful標準,還有像SOAP、ProtolBuf、MessageQueue等不同的協(xié)議與格式標準,它們都可以被作為服務之間通信的API。不同的API設計對服務使用的體驗差別會很大,除去技術原因對API協(xié)議的傾向性,在設計和評價API方面依然有許多值得注意的地方。
一個好的API設計應該在接口的元數(shù)據(jù)中向用戶提供盡可能多的有意義的業(yè)務信息,這里指的元數(shù)據(jù)包括例如API的名稱、參數(shù)、標簽等等用戶在不需要專門查看文檔就可以看到的內容。通常在各種不同的API協(xié)議里,總會存在一個相似的概念,就是目標路徑。比如RESTful的URL地址,ProtolBuf的消息類型嵌套結構,MessageQueue名稱里用斜線劃分Topic路徑的慣例等。以RESTful標準為例,可以試比較下面兩種URL地址的差異。
• 第一種:/users/123/orders/123
• 第二種:/orders?id=123&user_id=123
顯然前一種地址包含的信息更多,它告訴了訪問者訂單(orders)是屬于用戶(users)這個實體中的一個子實體,并且包含了一個潛在業(yè)務規(guī)則,即如果ID為123的這個用戶不存在了,那么查詢他下面的所有訂單信息也是不具有業(yè)務意義的。實體之間的嵌套關系可以通過領域建模過程中的聚合識別。當然,這種URL結構有時會導致很長的API路徑,但相比它所帶來的業(yè)務語義,我們認為還是值得的。
類似的語義化例子還有例如:
• PUT /services/questions/123
• PUT /services/questions/123/comments/123
這是售后服務部分的兩個API,分別用來更新提問的內容和提問的評論內容,它們也是按層級組織的。
有些通信協(xié)議中還提供了其他可以表明業(yè)務語義的元素。比如RESTful中使用GET/POST/PUT操作語義(查詢/創(chuàng)建/修改),以及HTTP返回值中的語義信息等。在實際設計API時充分利用這些協(xié)議特性,能夠使服務變得更加易用。
【微服務的架構設計】相關文章:
ARCH思科網(wǎng)絡服務架構設計考試主要內容05-31
系統(tǒng)架構設計模式大全08-11
系統(tǒng)架構設計師要素06-03
web系統(tǒng)分層架構設計06-17
RFID校園監(jiān)控系統(tǒng)架構設計06-20
SSH架構檔案管理系統(tǒng)設計06-21
航標業(yè)務系統(tǒng)架構的設計和實現(xiàn)06-10
適用高性能微服務架構的開源項目05-19