打走企業級落地微服務的攔路虎:數據

Markdown

▲掃碼報名活動
數人云11月Meetup報名開啓,
看中西方大神如何論道雲原生與微服務!數據庫

數人云上幾天給你們分享了:《踢開絆腳石:微服務難點之服務調用的解決方案》剖析了微服務的難點之一:服務調用的解決方案。今天再來跟你們聊聊微服務的另一個難點:數據。後端

在嘗試使用微服務架構的因由中,最主要的是容許團隊可以以不一樣的速度在系統的不一樣部分進行工做,並且儘可能將影響團隊的相關因素最小化。所以,咱們但願團隊可以自治,作出關於實現和運營服務最好的決策,而且能夠自由地進行更改。緩存

爲了得到這種自主權,須要「擺脫依賴」,不少人在某種程度上都引用了這句話,由於「每一個微服務都應該擁有和控制本身的數據庫,而不是兩個服務去共享一個數據庫。」這種理念是合情合理的,不要在服務之間共享一個數據庫是由於會遇到讀/寫模式衝突、數據模型衝突、協調性問題等等。安全

當構建微服務時,如何安全地將數據庫分切成多個小的數據庫?首先對於企業級構建微服務,須要明確如下內容:網絡

  • 域是什麼?它實現了什麼
  • 事物邊界在哪裏?
  • 微服務如何跨越邊界進行通訊?
  • 若是將數據庫打開,會怎樣?

什麼是域

這彷佛在不少地方都被忽視了,但在互聯網公司如何實踐微服務和傳統企業如何(或者可能由於忽視了這一點而失敗)實現微服務之間存在的巨大差別。架構

在構建微服務以前,以及它使用的數據(產品/消費等)的緣由,須要對數據的表示有一個明確清晰的理解,例如,在咱們能夠將信息存儲到一個數據庫中,關於TicketMonster的預約和它向微服務的遷移,須要瞭解「什麼是預約」,就如同在其餘領域裏須要瞭解什麼是帳戶、什麼是僱員、什麼是索賠等等。負載均衡

要作到這一點,須要深刻了解現實中的「IT」是什麼,例如,「什麼是書?」,試着停下來想想,由於這是一個很簡單的例子,試着去想什麼是一本書,那麼,如何在數據模型中表達這一點呢?框架

關於「什麼是一本書」沒有一個客觀的定義,所以,要回答這樣的問題,必須知道「誰在問問題,什麼是背景」,須要根據上下文去進行判斷。做爲人類,咱們能夠迅速(甚至是無心識地)解決着一理解的模糊性,由於在頭腦、環境和問題中都有一個背景,但計算機沒有,當構建應用並對數據建模時,須要明確地說出上下文,好比上面提到的企業擁有帳戶、用戶、預約索賠等等,這讓整個應用變得更爲複雜,而且會產生不少的衝突和模糊,因此,須要界限。異步

在哪裏劃定界限?域驅動設計社區中的工做幫助處理整個域的複雜性,在實體、值對象和集合中繪製一個有界的上下文,換句話說,構建和細化一個表明域的模型,整個模型包含一個定義上下文的邊界內,這些邊界最終會成爲微服務,或邊界內的組件最終會成爲微服務,抑或二者都是,不管哪一種方式,微服務都是關於邊界的,DDD(領域驅動設計)也是如此。分佈式

Markdown

數據模型(但願如何在物理存儲中表示的概念——注意,這裏的顯示差別)是由領域模型驅動的,當有這個邊界時,能夠知道並斷言,在模型中什麼是正確的,什麼是不正確的,這些界限也意味着必定程度的自制,有界上下文的「A」可能對「Book」有不一樣的理解,而不是有界上下文的「B」(例如,可能有界上下文A是搜索服務,搜索標題爲「Book」的標題,也許有界的上下文「B」是一種結帳服務,它根據你購買的數據(標題+副本)來處理交易)。

關於DDD:

一些人試圖複製Netflix,但他們只是複製了他們看到的那些,好比結果,而不是整個過程——Adrian Cockcroft,前Netflix首席雲架構師。

對於不一樣的來講,微服務也是不盡相同的,沒有一個硬性規定,只是根據自身實際狀況去權衡,複製對一家公司有用的東西,僅僅由於它在這一刻彷佛起到了做用,但跳過了過程以及其中的嘗試,不會起到決定性的做用,須要注意的一點是,你的公司並非Netflix,事實上,不管域在Netflix上有多麼的複雜,它都要比你的傳統企業簡單的多。一些互聯網公司之因此想實踐落地微服務,是由於它們的速度很是快,並且規模龐大(在Twitter上發佈一條推文很簡單,但爲5億用戶發佈推文和顯示Tweet流是很是複雜的),今天的企業將不得不面對領域和規模的複雜性,所以,接受這樣一個事實:對於每一個企業來講,都是不一樣的。

什麼是事務邊界

回到上文所設定的環境當中,須要一些如領域驅動設計這樣的東西來幫助咱們理解將要使用的模型來實現系統並在一個上下文中劃定這些模型的邊界,所以,接受客戶、帳戶、預約等可能對不一樣的有界上下文意味着不一樣的東西,但最終,可能會在體系結構中分佈這些相關的概念,但當發生變化時,須要一些方法協調這些不一樣的模型變化,須要對此進行解釋,但在這以前,首先須要肯定事務邊界。

然而不幸,做爲開發人員彷佛仍然在構建分佈式系統:仍然在查看一個單一的、關係型的、ACID的Database。也忽略了一個異步的、不可靠的網絡危險,也就是說,作一些事情如編寫一些奇特的框架,使咱們沒必要對網絡有任何瞭解(包括RPC框架、數據庫抽象也能夠忽略網絡),並嘗試用點對點同步調用(REST、SOAP、其餘CORBA,好比對象序列化RPC庫等等)來實現全部的東西。在不考慮權威和自治的狀況下構建系統,最終試圖經過許多獨立服務來解決分佈式數據問題,好比兩階段提交,或者把這些顧慮都忽略了?這種思惟模式會致使構建很是脆弱的系統,而這些系統不具備規模,若把它稱爲SOA、微服務、迷你服務等等,那就可有可無了。

交易邊界的意思是須要最小的單位原子性,這是業務不變量的,不管使用數據庫的ACID屬性來實現原子性仍是兩段提交等都不重要,關鍵是想要使這些事物邊界儘量小(理想的是單一對象的單一事物:Vernon Vaughn有系列的文章描述了這種方法,用DDD彙集),這樣就能夠伸縮了,當使用DDD術語構建領域模型時,會識別實體、值對象和彙集,在這個上下文中,彙集的對象是封裝其餘實體/值對象的對象,並負責執行不變量(在一個有界的上下文中能夠有多個聚合體)。

例如,假設有如下用例:

  • 容許客戶搜索航班
  • 讓客戶在特定航班上選擇座位
  • 容許客戶預約航班

可能會有三個有界的上下文:搜索、預約和票務(也許會有更多的支付、、待機、升級等等,但會將它縮小到這三個)。搜索負責顯示特定路線的航班和特定時間的路線(天數、時間等等),預約將負責在預約過程當中使用客戶信息(姓名、地址、常旅客號等)、做爲偏好和支付信息進行預約,票務將負責與航空公司的訂位,並簽發機票,在每一個有界的上下文中,但願肯定事務邊界,在那裏能夠執行約束/不變量,不會考慮跨界上下文的原子事務,若想要小的事務邊界(這是預約航班的一個很是簡化的版本),改如何建模呢?多是一個包含時間、日期、路線和像客戶、飛機以及預約這樣的實體航班集合?由於航班都有飛機、做爲、客戶和預約。爲創造預約,飛行總機負責跟蹤飛機、座椅等。這可能從數據庫內部的數據模型角度(帶有約束和外鍵的關係模型)或者在源代碼中建立一個不錯的對象模型(繼承/組合),但來看看會發生什麼:

Markdown

在全部的預約、飛機、航班等方面是否真的存在不變量,只是爲了創造一個預約?也就是說,若是在航班總數上增長了一架新飛機,是否應該在這一交易中包含客戶和預約?可能不會,在這裏所擁有的是一種聚合的聚合以及數據模型的便利,然而,事務的邊界太大了,若是對航班、做爲、預約等進行大量的更改,就會有巨大的事務衝突(不管是使用樂觀的仍是悲觀的鎖定都不重要),並且這顯然沒有規模(永遠不要介意訂單的不斷降低,由於航班計劃正在改變,這是一種糟糕的客戶體驗)。

若是打破了交易的界限,那就更小了。

也許預約、座椅可用性和航班是它們本身的獨立集合,預約封裝了客戶信息、首選項和支付信息,Seat可利用聚合封裝了飛機和飛機的配置,航班集合是由時間表、路線等組成的,但能夠在不影響航班時刻表和飛機/座位可用性的狀況下進行預約,從領域的角度看,但願可以作到這一點,不須要100%的嚴格一致性,但確實想要正確地記錄飛行時間表的變化,如管理員,飛行的配置,以及客戶的預約,那麼,如何實現「在飛行上預約一個座位,」這樣的事情呢?

在預約過程當中,可能會呼叫座椅可用性集合,並要求它在飛機上預約一個做爲,這個做爲預約將被實現爲一個單一的事務,例如(持有做爲23A),並返回一個預約ID,能夠將這個預約ID於預約聯繫起來,而且提交,知道這個座位是在一個「預約」的每個(保留一個座位,並接受預約)都是單獨的事務,而且能夠獨立地進行,而不須要任何兩階段提交或兩階段鎖定。注意,這裏使用「預約」是一個業務需求,不作座位分配,只保留座位,這個需求可能須要經過模型的迭代而被束縛,由於最初使用用例的語言可能只是簡單地說「容許客戶選擇一個座位」,開發人員能夠試着推新,這個需求意味着「從生育的座位中挑選,把它分配給客戶,從庫存中移除它,而且不出售比座位更多的票」。這將是額外的,沒必要要的不變量,這會給事務模型增長額外的負擔,而業務模型實際上並非不變的,在沒有徹底的座位分配狀況下,這個業務固然能夠接受預約,甚至是超額銷售機票。

Markdown

上圖爲一個示例,容許真正的域引導使用更小的、簡化的、徹底原子的事務邊界來處理單個聚合,如今必須糾正這樣一個事實:全部的個體交易都須要在某一時刻彙集在一塊兒,數據的不一樣部分涉及到(例如,建立了一個預約和座位預約,但這些都不是經過固定的交易來得到登機牌/機票等)。

微服務如何跨邊界通訊

想要保持真正的業務 不變性,使用DDD能夠選擇將這些不變量建模爲聚合,並使用單個事務進行聚合,在某些狀況下,能夠在單個事務中更新多聚合(跨單個數據或多個數據庫),但這些場景多是例外,仍然須要在聚合之間保持某種形式的一致性(最終在有界的上下文之間),那麼應該怎麼作呢?須要理解的一件事是:分佈式系統是很是挑剔的,若是能在有限的時間內對分佈式系統中的任何東西作出任何保證(事情將會失敗,事情是不肯定的,或者彷佛失敗了,系統有非同步的時間邊界等等),那麼爲何要嘗試去解決它呢?若是接受並將其放入領域的一致性模型中會怎樣呢?若是說「在必要的事務邊界之間,能夠與數據和域的其餘部分進行協調,並在之後的某個時間點保持一致」?

正如一直說的,對於微服務,咱們重視自主權,認爲可以獨立於其餘系統(在可用性、協議、格式等方面)進行更改,時間的解耦和任何有限制的時間之間任何保證之間的任何保證,都容許真正實現這種自治(這不是計算機系統特有的,所以在事務邊界和有界上線問之間,使用事件來保持一致性,事件是不可變的結構,它捕獲了一個有趣的時間點,應該向對等點廣播,同伴們會聆聽它們感興趣的事件,並根據這些數據作出的決定更新本身的數據等等。)

繼續以訂機票爲例,當一個預約經過一個Acid風格的交易存儲時,如何結束它?這是前面提到的「退票界」的背景,預約有界上下文將發佈相似「New Booking建立」這樣的事件,而票務綁定上下文將會消耗該事件,並繼續與後端(多是遺留的)票務系統進行交互,這顯然須要某種集成和數據轉換,這是Apache Camel很是擅長的。也引出了一些其餘的問題,如何對數據庫進行寫操做,並以原子的方式將其發佈到隊列/消息傳遞設備中?

若是在事件之間有排序需求/因果需求呢?每一個服務的一個數據庫呢?

理想狀況下,聚合會直接使用命令和域事件(做爲第一個類公民,也就是說,)任何操做都是做爲命令來實現的,任何響應都是做爲對事件的響應來實現的)能夠更清晰地映射使用內部上下文和上下文之間使用的事件之間關係,能夠將事件(即Newbooking建立)發佈到消息隊列中,而後讓偵聽器從消息隊列中使用該隊列,並將其插入到數據庫中,而無需使用Xa/2pc事務,而不須要將其插入到數據庫中,能夠將事件插入到一個專用的事件存儲中,它就像數據庫和消息發佈-訂閱主題同樣(這多是首選路由),或者能夠繼續使用一個ACID數據庫,並將該數據庫的變化流到一個持久的、複製的日誌中,就像Apache卡夫卡同樣,使用相似於Debezium的東西,並使用某種事件處理器來推斷事件,不管哪一種方式,關鍵是想要在時間事件中與不可變點之間的邊界進行通訊。

Markdown

這帶來了一些巨大的優點:

  • 避免了跨邊界且昂貴潛在的不可能的事務模型
  • 能夠對系統進行更改,而不妨礙系統的其餘部分(時間和可用性)的進展
  • 能夠將數據存儲在本身的數據庫中,同時使用適合於服務的技術
  • 能夠在空閒時更改模式/數據庫
  • 變得更加可伸縮、容錯、靈活

必須更加關注CAP定力和選擇的技術來實現存儲/隊列

  • 須要注意的是,這也有一些缺點:
  • 更加複雜
  • 難以調試
  • 因爲看到事件時有延遲,因此不能對其餘系統所知道的內容做出任何假設
  • 必須更加關注CAP定理和選擇的技術來實現隊列/存儲

從這種方法中產生另外一個有趣的概念是,可以實現一種稱爲「命令查詢分離責任」的模式,在這種模式中,將讀模型和編寫模型分離到單獨的服務中,請記住,咱們對互聯網公司沒有很是複雜的領域模型感到遺憾,這在他們的編寫模型中很明顯(例如,在一個分佈式日誌中插入一條Tweet)。然而,他們的閱讀模式因其規模而變得很是複雜,CQRS幫助分離了這些問題,另外一方面,在企業中,編寫模型可能很是複雜,而讀取模型多是簡單的平面選擇查詢和扁平DTO對象,CQRS是一種強大的關注點隔離模式,一旦有了適當的邊界和在彙集和有界上下文之間進行數據更改的好方法,就能夠對其進行評估。

那麼服務只有一個數據庫,而且不與其餘服務共享呢?在這個場景中,可能會偵聽事件流的偵聽器,並可能將數據插入到一個共享數據庫中,而主聚合可能最終會使用這些數據,這個「共享數據庫」很是好,記住,沒有規則,只有權衡,在這種狀況下,可能會有不少服務協同同一個數據庫一塊兒工做,只要團隊擁有全部的過程,就不會否認自治的優點,當聽到有人說「微服務應該有本身的數據庫,而不是別人分享」時,你能夠回答:好吧,有點。

打開數據庫

若是將使用事件/流來進行全部的事情,而且永遠的持久化這些事件呢?若是說數據庫/緩存/索引實際上只是過去發生的事件的持久日誌/流的物化視圖,而當前狀態是全部這些事件的左摺疊?

這種方法帶來了更多的好處,能夠經過事件(上面列出的)來增長交流的好處:

  • 如今能夠將數據庫看作是記錄的:「當前狀態」,而不是真正的紀錄
  • 能夠引入新的應用,從新閱讀過去的事件,並根據「將要發生的事情」來檢查它們的行爲
  • 免費完善審計日誌記錄
  • 能夠引入應用的新版本,並經過從新播放事件對其執行很是詳盡的測試
  • 能夠更容易地關於數據庫版本/升級/模式變化的事件重演到新數據庫中
  • 能夠遷移到全新的數據庫技術(例如,可能發現已經超越了關係數據庫,而且但願切換到專門的數據庫/索引)

Markdown

TM體系結構

當在aa.com、Delta.com或united.com上預約航班時,會看到其中的一些概念,當選擇一個座位時,並無實際地分配它。當訂機票時,實際上並無收到實體票,以後會收到一封電子郵件,通知已經被確認,你是否有過一次飛機換班的經歷,並分配到一個不一樣的座位?或者是去登機口,聽到工做人員要求放棄座位,由於機票已經超出預約座位,這些都是事務邊界的例子,最終的一致性、補償事務,甚至連工做中的道歉都屬於。

Moral Of The Story

這裏指的是:數據、數據集成、數據邊界、企業使用模式、分佈式系統理論、時間等等,都是微服務的難點(由於微服務是真正的分佈式系統)在技術上看到了太多的困惑(「若是使用Spring引導,我正在作微服務」,「須要解決服務發現,在雲計算以前負載均衡」,必須有一個每一個微服務的數據庫)和關於使用微服務的無用理論,別擔憂,由於一旦大的供應商來賣給你全部的高級產仍然須要作上面列出的那些困難部分。

原文做者:Software
原文連接:http://www.tuicool.com/articl...

相關文章
相關標籤/搜索