微服務架構基本原理學習筆記(二)

来源:https://www.cnblogs.com/jaxu/archive/2023/06/01/17448186.html
-Advertisement-
Play Games

上一篇:微服務架構基本原理學習筆記(一) 三、微服務架構 從一個已有的單體架構的應用程式開始進行微服務架構的重構往往是一個不錯的選擇。隨著業務量和功能的增加,我們可以考慮使用微服務架構來擴充應用程式中原有的功能,或者每次添加新功能時,都為其創建一個新的微服務。這比從一開始就選擇使用微服務架構進行設計 ...


上一篇:微服務架構基本原理學習筆記(一

三、微服務架構

  從一個已有的單體架構的應用程式開始進行微服務架構的重構往往是一個不錯的選擇。隨著業務量和功能的增加,我們可以考慮使用微服務架構來擴充應用程式中原有的功能,或者每次添加新功能時,都為其創建一個新的微服務。這比從一開始就選擇使用微服務架構進行設計要相對容易一些,因為微服務架構的好處通常不會體現在小型項目中。所以,考慮讓項目持續迭代一段時間,直到我們能夠非常清晰地確定服務的邊界,通過微服務架構來進行功能的劃分。

  因此,對於每一個微服務,我們都需要明確它們各自的職責,並定義公共介面。

每個微服務管理各自的數據

  前面我們已經介紹過,微服務是自治的並且可以單獨運行和部署,而實現這一特性的關鍵就是確保每個微服務都擁有自己的數據存儲。也就是說,微服務架構中不允許出現多個微服務共用同一個數據的情況。任何想要訪問其它微服務中數據的情況都應該通過公共介面來完成。

  你可能在想,如果沒有一個中心的共用資料庫,如果保證數據的一致性呢?尤其是對於事務而言,數據的一致性至關重要。在微服務架構中,我們不能在單個資料庫事務中去更新來自不同的微服務中的數據,我們要麼通過較為複雜的分散式事務來實現這一功能,要麼通過微服務自己來保證數據的一致性。而後者是通常較為推薦的做法。這就意味著,我們無法保證在一個相對較短的時間內數據的一致性,而是需要等待一段時間之後才能獲得整體狀態一致的數據。具體的操作是,當其中一個微服務的數據更改時,在一個短暫的時間內其它微服務中的數據無法與其保持一致,這個時候,你需要有一種機制能夠應對這種暫時性的數據非一致性,直到所有微服務中的數據最終獲得一致性。在微服務中使用緩存機制是一個不錯的選擇,這可以大大減少從多個微服務聚合數據以執行單個操作的成本。

  查看Github上的eShopOnContainers的微服務架構圖,我們可以看到其中不同的微服務都有各自的數據存儲。例如Identity微服務使用了SQL Server數據,Ordering微服務也使用了SQL Server資料庫,但它和Identity微服務使用的不是同一個資料庫,它們之間也不能相互訪問。Basket微服務使用的則是完全不同的Redis緩存資料庫。

  現在考慮一個實際場景,Ordering微服務中有一個OrderItem實例,它代表用戶的一個訂單,其中包括ProductId、ProductName和UnitPrice。Catalog微服務中有一個CatalogItem實例,它代表商品信息,其中包括ID、Name和Price。當用戶購買商品時,Ordering微服務中不僅會記錄商品的ID,同時還會將商品的名稱和價格複製到自己的資料庫中。那麼,如果將來Catalog微服務中的商品信息發生了變化,這個數據並不一定會及時地同步到Ordering微服務中。那麼,如果我們只在Ordering微服務中存儲商品ID,而當需要的時候通過API去請求Catalog微服務以獲取商品的詳細信息豈不是更好?不一定!

  Ordering微服務中商品數據的冗餘反映的是用戶購買商品時的情形,它與存儲在Catalog微服務中商品現在的信息並非一致。Ordering微服務並不關心商品當前的價格和名稱,它關心的是用戶下單時商品的價格和名稱。所以事實上,這不是真正的數據冗餘,這些數據在不同的微服務上下文中具備不同的含義。微服務之間的這種數據冗餘以及不一致性不僅不會導致系統出現問題,相反,它是由不同的業務需求所決定的。

微服務的組成部分

  微服務不一定是在單個伺服器或虛擬機上運行的單個進程,它通常至少有兩部分組成:一個由代碼構成的WebAPI,而另一個則是資料庫。所以這至少是兩個不同的進程,而且它們通常並不在同一臺伺服器或虛擬機中運行。進一步地,如果我們對服務進行橫向擴展,並同時對資料庫進行分片配置,那麼一個微服務甚至會在好幾個不同的伺服器或虛擬機中運行,而且它可能還需要某種定時任務來執行數據維護,並監聽和觸發各種不同的消息。因此,實際上一個微服務可能會涉及到多個不同的伺服器和進程,所有的這些部分一起構成了一個微服務。

  我們對比看一下eShopOnContainers的微服務架構圖,其中的Ordering微服務包含一個名為Ordering.API的WebAPI,另外還有一個名為Ordering.BackgroundTasks的後臺任務,這是兩個獨立運行的進程。我們不必讓微服務的所有代碼都在一個進程中運行。

  從概念上講,每個微服務及其公共介面都有明確的邊界定義,數據只能通過這些公共介面訪問。

每個微服務都是可獨立部署的

  微服務可獨立部署和存在,這就意味著依賴於該微服務的客戶端程式不需要同時升級。要做到這一點,你必須確保微服務的公共介面始終保持對舊客戶端程式的相容。實際上,我們通常把這些公共介面稱之為微服務與客戶端之間的協議,協議是不能單方面更改的。你可能會問,隨著業務和需求的增長,這些公共介面怎麼能永遠保持不變呢?最簡單的解決辦法是永遠只對公共介面進行增量修改,例如增加新的介面,或在已有的介面上對數據增加新的屬性。

  如果不可避免地要對公共介面進行重大調整,可以考慮下麵兩種實踐方式:

  一是讓開發客戶端的團隊等待新的微服務上線之後再更新客戶端程式,以確保對微服務的調整不會影響到舊客戶端程式的運行。

  另外一個推薦的做法是針對不同版本的客戶端程式創建自動化測試,並將其加入到持續集成構建中,這樣每次部署之前,如果有自動化測試未完成,構建就會失敗,我們就可以查找原因並分析是否存在相容性問題。

  需要註意的一點是,有一種模式可以引入客戶端和微服務都使用的共用代碼,從而可以非常方便地生成一個客戶端包來簡化微服務的調用。我們應該儘量避免使用這種方式,不要將微服務的開發和測試與客戶端緊密耦合在一起,因為這會導致客戶端對微服務的強依賴而迫使它們同時升級。

如何確定微服務的邊界

  如何確定微服務的邊界是一項比較困難的事情,因為錯誤的微服務邊界定義會導致後期系統性能的下降,並且一旦這些微服務部署到了生產環境,後期再做調整就比較困難了。因此,在項目開始之前,值得我們花一些時間來認真考慮如何確定微服務的邊界。

  我們前面也介紹過,從已有的單體架構的應用程式開始進行微服務架構設計會使任務變得相對容易一些。你會發現其中有一些模塊本身就與應用程式的其它部分鬆散耦合,它們之間通過比較清晰的介面訪問數據,這些模塊比較容易轉換成微服務。

  另外一種方式是從資料庫層面著手,看看從某些概念上是否可以將部分表組合在一起,形成一個相對獨立的部分。因為每個微服務都擁有自己獨立的數據,所以從資料庫層面抽取相對獨立的部分也是一個好的想法,我們應該儘量避免從多個不同的微服務之間獲取數據。

  微服務始終應該圍繞著業務來進行組織,這方面可以參考領域驅動設計(Domain Driven Design)的概念,它推薦從應用程式的上下文中來確定邊界,併為其定義模型,這意味著不同的微服務將使用不同的模型,即使它們使用的數據看起來沒什麼區別,但對應的業務場景卻不同。

  在eShopOnContainers微服務架構中,Ordering微服務和Catalog微服務儘管都與商品信息有關,但它們處在不同的上下文中,因此它們實際上具有不同的屬性,可以自由地為同一條信息使用不同的名稱。

  在確定微服務的邊界時,你可能會遇到一些陷阱,例如對所有資料庫中的業務表進行簡單的包裝並生成對應的CRUD服務,這些服務充其量只能稱之為資料庫表的實體類,而不能稱之為微服務,因為它們只具有添加和更新實體的方法,而並沒有將與實體相關的業務邏輯包含進來。另一個導致微服務邊界模糊的情況是,當多個微服務相互之間存在迴圈依賴關係時,會導致頻繁的相互通信,我們應該儘量避免這種情況的出現。

  讓我們詳細瞭解一下eShopOnContainers微服務架構中的具體實現,來看看以上這些原則在實際應用中是如何體現的。

Catalog Microservice 存儲商品的詳細信息。
Basket Microservice 跟蹤和存儲客戶的購物籃信息。
Ordering Microservice 處理客戶的訂單信息。
Identity Microservice 用於處理用戶身份認證。
  1. 職責分離,有助於提高彈性。即使Ordering微服務不可用,依然不影響客戶瀏覽商品信息並將其添加到購物車。
  2. 不同的微服務都被設計為處理不同的數據量和訪問模式。Catalog微服務需要支持靈活的查詢,以滿足客戶根據各種不同的查詢方式來搜索到想要購買的商品。所以,Catalog微服務需要將數據存儲在支持大量豐富查詢的資料庫中以滿足業務的需要,例如這裡選擇了SQL Server。而Basket微服務只需要存儲比較短暫的數據,這些數據甚至都不需要寫入資料庫,所以這裡使用了Redis記憶體緩存來保存客戶購物車中的數據。Ordering微服務用來處理用戶的訂單信息,因此對數據的可靠性有非常嚴格的要求。另外它還需要處理一些敏感數據,例如客戶的收穫地址和付款信息等,所以對安全性也有非常高的要求。Identity微服務用來處理身份驗證,我們將在後面的微服務安全部分對其進行詳細說明。
  3. 這不是唯一的確定微服務的方式,你可以根據不同的業務需求對其中的功能進行重新組合。就目前來看,這種設計還不錯。

四、構建微服務

  當我們開發微服務應用程式時,我們希望它能運行在不同的環境中,例如開發人員希望在自己工作的電腦上運行和調試代碼;測試人員希望在臨時搭建的測試環境中運行,當然也可能是在雲上運行;當微服務正式發佈之後,它會在我們的生產環境中運行。那麼我們如何托管微服務,使其非常方便地運行在不同的環境中呢?

  下麵列出了幾種不同的方式:

  1. 傳統的方式是使用虛擬機。你可以為每一個微服務選擇一臺虛擬機,當然如果你的微服務數量特別多的話,這麼做成本可能會比較高。你也可以選擇將多個微服務打包到一臺虛擬機上,但是你需要為其中的每一個微服務安裝不同的框架和依賴包,這會讓初始化工作變得較為繁瑣。
  2. 第二種方式是選擇PASS(Platform as a Service)平臺。有許多雲提供商都提供了微服務的托管服務,你只需要專註於微服務的具體實現, PASS平臺負責微服務的管理和基礎設施,並可以為每個內置負載均衡的微服務提供自動擴展和DNS條目,另外還有標準的安全和監控功能等。
  3. 第三種方式是選擇使用容器。這是當下最流行的方式之一。容器可以將應用程式及其所有的依賴項都打包在一起,然後非常方便地在任何容器主機上輕鬆移植並運行。容器主機可以是本地工作的電腦,也可以在雲上,這大大簡化了開發和部署的任務。示例應用程式eShopOnContainers就使用了容器來構建微服務,具體步驟可以查看Github上的文檔。容器可以使構建和運行微服務的過程變得簡單,如果我們單獨為每一個微服務安裝所有依賴的軟體並配置所有內容,將會是一個非常痛苦的過程,可能需要好幾天的時間,期間也可能會遇到各種各樣的問題,而容器會使這一切變得非常簡單。而且,許多開發工具也允許將調試器附加到容器內運行的代碼,這對開發人員debug微服務也帶來了很多的便利。

如何開始創建一個微服務

  首先,你需要一個用來保存源代碼的倉庫,例如Github。雖然從技術上來說你可以將所有微服務的代碼都保存在同一個源代碼倉庫中,但是如果微服務的數量很多,這會大大增加微服務間的耦合程度,這顯然違背了微服務設計的初衷。

  其次,我們還需要一個能夠自動化持續集成構建微服務的系統,每當我們提交代碼到源代碼管理器的時候,它都會自動構建一個新的微服務版本,同時還會執行自動化測試,如果測試未通過,則新的構建就會失敗,並自動發消息給相應的開發人員。測試是構建微服務的一個非常重要的部分。

測試微服務

  測試是構建微服務中一個非常關鍵的部分。在構建微服務的過程中,有幾種不同類型的測試:

  1. 單元測試。單元測試是針對代碼級別的,通常運行比較快。在單元測試中,我們要儘可能保證高的代碼覆蓋率,尤其是那些針對特定業務的邏輯和模塊。
  2. 集成測試。也叫做服務級別的測試,是針對於單個微服務的測試。通常我們需要將單個微服務部署到伺服器中,併進行相應的配置,然後通過調用暴露出來的公共介面來測試微服務的功能是否正常。相對於單元測試而言,集成測試更難編寫,但它們對於微服務的質量和穩定性來說非常有價值,所以值得我們花時間去創建一個測試框架,然後為每個微服務創建集成測試。這些測試也應該作為自動化構建微服務的一部分。
  3. 端到端測試。這些測試是模擬生產環境中所有運行的微服務,我們可以通過UI界面來執行一些關鍵的業務操作流程,以保證儘可能多的微服務之間的協同工作是否能否達到預期的目標。這部分的測試在編寫和維護方面更加困難,而且往往很容易出錯,因為任何一個底層邏輯的改變都有可能導致測試失敗。你也可以嘗試其它類型的測試來驗證儘可能多的功能,但往往端到端的測試在驗證某些關鍵功能方面仍然具有價值,例如在冒煙測試中快速檢測系統關鍵點的功能是否正常。

微服務模板

  在創建微服務時,並不是每次都從零開始。從一個標準模板開始創建微服務可以省去很多工作。你可以根據需要在自己的代碼倉庫中維護微服務模板,也可以在某個特定的微服務的基礎上進行改寫。有許多通用的功能都可以標準化,例如:

  • 日誌(Logging),它將系統中所有微服務的日誌集中管理。
  • 健康檢查(Health Checking),如果每個微服務都可以報告自身的運行狀態,告知是否正在運行,是否可以與依賴的其它微服務進行通信,這將是一個不錯的設計。
  • 配置(Configuration),讓所有微服務都採用統一的方式進行配置不失為一個好的想法。
  • 身份認證(Authentication),我們可以使用一些標準的身份認證機制,這可以降低開發過程中一些錯誤配置而導致我們的微服務存在安全漏洞。
  • 構建腳本(Build Scripts),使用標準的構建方法可以避免我們少走彎路。例如eShopOnContainers中使用容器,每個微服務都使用一個Docker文件來生成容器鏡像。

  當然,你可以根據需要將某些特定的功能添加到模板中。模板的好處是提供了開箱即用功能(Out of Box),這大大減少了啟動和運行微服務所需的時間,並且還可以確保系統中所有微服務的一致性。不過這種所謂的一致性不應該限制微服務所使用的技術,例如我們不應該限制所有的微服務都使用同一種編程語言,儘管這可以給開發人員在不同的微服務之間工作帶來便利,但是我們不應該限制這種技術自由,微服務的開發團隊可以自由選擇最佳的開發工具。我們的目標是使微服務開發團隊儘可能高效地工作,將時間花在實現微服務的業務需求上,而不是圍繞微服務技術本身。

  每個開發人員都應該具備獨立處理和運行一個微服務的能力,例如在集成測試環境中檢查各個功能點是否正常。而且,每個開發人員也應該能夠在一個完整的系統中測試他們開發的某個功能。開發人員可以在本地環境中運行所有的內容,也可以在雲上直接訪問整個系統。但是無論採用哪種方式,工作流程都應該儘量簡單並可以自動完成。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天想聊一下分庫分表,因為對於快速增長的業務來說,這個是無法迴避的一環。之前我在做商城相關的SAAS系統,商品池是一個存儲瓶頸,商品池數量會基於租戶增長和運營變得指數級增長,短短幾個月就能漲到幾千萬的數據,而運營半年後就可能過億。而對於訂單這種數據,也會跟著業務的成長,也會變得愈發巨大。 ...
  • > 你準備好面試了嗎?這裡有一些面試中可能會問到的問題以及相對應的答案。如果你需要更多的面試經驗和麵試題,關註一下"張飛的豬大數據分享"吧,公眾號會不定時的分享相關的知識和資料。 [TOC] ## 1、 HDFS 中的 block 預設保存幾份? 預設保存3份 ## 2、HDFS 預設 BlockS ...
  • 雖然漢字#起名名字#的資料庫已經有一些,比如7千多漢字起名參考大典ACCESS資料庫、漢字起名中文起名寶寶起名ACCESS資料庫,但是今天發現了一個資料庫,他是在《7千多漢字起名參考大典》的基礎上增加了30萬個男孩女孩的名字實例。非常適合於比如固定了名字的第二個字,取第三個字時一查就有參考。 漢字表 ...
  • 燈謎,即寫在彩燈上面的謎語,又叫“燈虎”。猜燈謎又叫“射燈虎”。謎語來源於民間口謎,後經文人加工成為謎,它在中國源遠流長。春秋戰國時期,出現了“隱語”或“庾辭”。秦漢時則成為一種書面創作。三國時代,猜謎盛行。在宋代出現了燈謎。人們將謎條繫於五彩花燈上,供人猜射。明清時代,猜燈謎在民間十分流行。 按“ ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 面試官:“HTTPS的加密過程你知道麽?” 我:“那肯定知道啊。” 面試官:“那你知道什麼情況下 HTTPS 不安全麽” 我:“這....” 越面覺得自己越菜,繼續努力學習!!! 什麼是中間人攻擊? 中間人攻擊(MITM)在密碼學和電腦 ...
  • # VuePress2.0構建項目文檔系統 參考TerraMours 官網。[https://terramours.site/](https://terramours.site/) 文件結構參考: ![image-20230530170541496](https://www.raokun.top/u ...
  • 當我們在電腦中使用浮點數進行計算時,特別是在使用二進位表示浮點數時,可能會出現舍入誤差。這是由於電腦使用有限的位數來表示浮點數,而某些十進位數無法精確地表示為有限的二進位數。 0.1 和 0.2 都是無限迴圈的二進位數,在轉換為浮點數時並不能完全準確地表示。將它們相加時,可能會出現舍入誤差。因此 ...
  • ## 1. 背景 - 業務背景:CRM系統隨著各業務條線對線索精細化分配的訴求逐漸增加,各個條線的流向規則會越來越複雜,各個條線甚至整個CRM的線索流轉規則急需一種樹形的可視化的圖來表達。 - 技術背景:在開發之前考慮了三種方案,原生canvas、fabric以及G6,三種方案各有優劣勢 |  | ...
一周排行
    -Advertisement-
    Play Games
  • 下麵是一個標準的IDistributedCache用例: public class SomeService(IDistributedCache cache) { public async Task<SomeInformation> GetSomeInformationAsync (string na ...
  • 這個庫提供了在啟動期間實例化已註冊的單例,而不是在首次使用它時實例化。 單例通常在首次使用時創建,這可能會導致響應傳入請求的延遲高於平時。在註冊時創建實例有助於防止第一次Request請求的SLA 以往我們要在註冊的時候實例單例可能會這樣寫: //註冊: services.AddSingleton< ...
  • 最近公司的很多項目都要改單點登錄了,不過大部分都還沒敲定,目前立刻要做的就只有一個比較老的項目 先改一個試試手,主要目標就是最短最快實現功能 首先因為要保留原登錄方式,所以頁面上的改動就是在原來登錄頁面下加一個SSO登錄入口 用超鏈接寫的入口,頁面改造後如下圖: 其中超鏈接的 href="Staff ...
  • Like運算符很好用,特別是它所提供的其中*、?這兩種通配符,在Windows文件系統和各類項目中運用非常廣泛。 但Like運算符僅在VB中支持,在C#中,如何實現呢? 以下是關於LikeString的四種實現方式,其中第四種為Regex正則表達式實現,且在.NET Standard 2.0及以上平... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他們的程式記憶體會偶發性暴漲,自己分析了下是非托管記憶體問題,讓我幫忙看下怎麼回事?哈哈,看到這個dump我還是非常有興趣的,居然還有這種游戲幣自助機類型的程式,下次去大玩家看看他們出幣的機器後端是不是C#寫的?由於dump是linux上的程式,剛好win ...
  • 前言 大家好,我是老馬。很高興遇到你。 我們為 java 開發者實現了 java 版本的 nginx https://github.com/houbb/nginx4j 如果你想知道 servlet 如何處理的,可以參考我的另一個項目: 手寫從零實現簡易版 tomcat minicat 手寫 ngin ...
  • 上一次的介紹,主要圍繞如何統一去捕獲異常,以及為每一種異常添加自己的Mapper實現,並且我們知道,當在ExceptionMapper中返回非200的Response,不支持application/json的響應類型,而是寫死的text/plain類型。 Filter為二方包異常手動捕獲 參考:ht ...
  • 大家好,我是R哥。 今天分享一個爽飛了的面試輔導 case: 這個杭州兄弟空窗期 1 個月+,面試了 6 家公司 0 Offer,不知道問題出在哪,難道是杭州的 IT 崩盤了麽? 報名面試輔導後,經過一個多月的輔導打磨,現在成功入職某上市公司,漲薪 30%+,955 工作制,不咋加班,還不捲。 其他 ...
  • 引入依賴 <!--Freemarker wls--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> ...
  • 你應如何運行程式 互動式命令模式 開始一個互動式會話 一般是在操作系統命令行下輸入python,且不帶任何參數 系統路徑 如果沒有設置系統的PATH環境變數來包括Python的安裝路徑,可能需要機器上Python可執行文件的完整路徑來代替python 運行的位置:代碼位置 不要輸入的內容:提示符和註 ...