學習微服務首先要了解爲何使用微服務

單體的優缺點前端

單體應用就是將應用程序的全部功能都打包成一個獨立的單元,最終以一個WAR包或JAR包存在,沒有外部的任何依賴,裏面包含DAO,Service、UI等全部的邏輯。單體應用有如下優勢:java

  • 便於開發:只需藉助IDE的開發、調試功能便可完成
  • 易於測試:只須要經過單元測試或瀏覽器便可完成測試
  • 易於部署:打包成單一可執行jar包,執行jar包便可完成部署

不幸的是,這種簡單的單元有很大的侷限性。應用程序隨着業務需求的迭代,功能的追加擴展,最終成爲一個龐然大物。變得更加複雜,邏輯耦合嚴重,難以理解,團隊開發 人員職責不清,部署困難,迴歸測試成本巨大,交付效率大大下降,總結下來,單體應用有一下缺點:redis

1. 複雜性高sql

  • 代碼難以理解
    在業務規模和團隊規模發展的必定階段,這些不足表現的更加明顯,單體架構的不足首先表如今複雜性上, maven模塊增多,多個模塊耦合在一塊兒,代碼結構混亂,使得團隊成員沒有一我的理解整個代碼邏輯;數據庫

  • 難以理解致使代碼質量低,複雜性進一步增長
    難以理解致使代碼複用度下降,由於你不知道哪些能夠複用的;即使修改,影響範圍也很差肯定,這致使這樣開發寧願新建一個新方法和新的類,進一步致使重複代碼越積越多;後端

  • 代碼難以被修改和重構
    不理解代碼固然也就寫不出高內聚低耦合的代碼,和代碼質量持續降低;複雜性進一步增長隨着複雜度的增長,耦合度愈來愈高,代碼牽一髮而動全身,代碼已經很難修改和重構了
  • 團隊職責不清晰
    高度耦合的單體工程使得邏輯邊界模糊不清,新的業務需求開發任務沒法有效分配到人,團隊人員職責不清晰,溝通成本增長。

2.交付效率低api

  • 構建和部署耗時長,難以定位問題,開發效率低
    代碼量比較龐大,首先是編譯耗時變長,開發調試將大部分時間花在從新編譯上,代碼量的增長又很難定位bug,致使開發效率進一步下降,在代碼合併過程當中極易遇到代碼衝突,又花上很多時間用在解決代碼衝突上;這都是致使開發效率低下的因素;
  • 代碼複雜和變動影響難以理解,須要數天完成全量測試
    當咱們開發完一個新的功能或者修復一個bug,代碼的變動影響是很難預估的,因此每次發佈以前都要進去全量功能的迴歸測試;
  • 全量部署耗時長、影響範圍廣、風險大,發佈頻次低
    正由於這種全量部署耗時長、影響範圍廣、風險大,致使咱們將不少功能和修復彙集在一塊兒進行開發完成,這致使了產品發佈頻次下降,新的功和更換的體驗能不能及時呈現給用戶,甚至被競爭對手趕超。
    3.伸縮性(scalable)差瀏覽器

  • 單體只能按總體橫向擴展,沒法分模塊垂直擴展安全

  • IO密集型模塊和CPU密集型模塊沒法獨立升級和擴容
    業務模塊對資源的需求是不同的,因爲全部模塊部署到一塊兒,單體架構IO密集型模塊和CPU密集型模塊沒法獨立升級和擴容的,好比圖片壓縮,加解密這些 都是cpu資源密集的應該升級CPU,而IO密集型的模塊好比日誌收集服務IO操做比較多須要更大的內存,使用好比SSD性能更好的磁盤。
    4. 可靠性差性能優化

  • 一個bug有可能引發整個應用的崩潰
    因爲全部模塊都是部署在一個實例中,一個bug會引發整個應用的崩潰,好比一個不重要的模塊的內存泄露就將會致使全部應用實例一個個crash掉
    5.阻礙技術創新

  • 受技術棧限制,團隊成員使用同一框架和語言
    受技術棧限制,團隊成員必須使用同一框架和語言,模塊得不到拆分,不能使用新的語言和框架;
  • 升級和變革技術框架變得困難
    當有符合業務場景的新技術產生或者新版本時,升級和變革技術框架所帶來的重構成本和風險變革很高
  • 嘗試新語言變得困難
    想嘗試新的語言也變得很困難,由於開發成本的上升,重構和新需求迭代沒法協調,因此最終只能是妥協繼續使用原來的框架和語言
    那麼如何解決單體的不足呢,經過遷移到微服務架構來解決,咱們看一下什麼是微服務。

那麼如何解決單體的不足呢,經過遷移到微服務架構來解決,咱們看一下什麼是微服務。

微服務的定義

微服務架構:將單體應用拆分爲多個高內聚低耦合的小型服務,每一個小服務運行在獨立進程,由不一樣的團隊開發和維護,服務間採用輕量級通訊機制,獨立自動部署,能夠採用不一樣的語言及存儲。

圖片描述

咱們經過上圖來看下單體架構到微服務架構的對比。此圖是一個簡單電商單體到微服務架構的演進圖,單體架構整個團隊維護開發一個大工程及一個單庫,到了微服務架構,用戶請求通過API Gateway被路由到下游服務,服務之間以輕量級通訊協議進行通訊,服務經過註冊中心發現彼此,每一個服務都有專門的開發維護團隊,每一個服務對應獨立的數據庫,服務獨立開發,獨立部署和上線。 接下來咱們總結下微服務的優勢。

微服務的優勢

  • 易於開發與維護

    • 微服務相對小,易於理解
    • 啓動時間短,開發效率高
  • 獨立部署
    • 一個微服務的修改不須要協調其它服務
  • 伸縮性強
    • 每一個服務均可以在橫向和縱向上擴展
    • 每一個服務均可按硬件資源的需求進行獨立擴容
  • 與組織結構相匹配
    • 微服務架構能夠更好將架構和組織相匹配
    • 每一個團隊獨立負責某些服務,得到更高的生產力
  • 技術異構性
    • 使用最適合該服務的技術
    • 下降嘗試新技術的成本

微服務的挑戰

沒有任何技術是銀彈,微服務也是如此 ,都或多或少有一些缺點和問題。那麼咱們就必須針對這些問題一一解決,也是咱們接下來章節重點去作的. 我首先面對就是要將單體拆分紅多個服務。
1.服務拆分

  • 微服務拆分原則:領域模型、限定上下文、組織架構、康威定律
    現實中沒有一個具體明確的方法能夠將拆分一步到位,而是遵照必定的原則,好比根據領域模型、組織架構、單一職責這些進行拆分在拆分的過程當中還要結合經驗判斷,而且隨着需求迭代,架構持續優化演進,優化服務的拆分。

  • 每一個微服務擁有獨立數據庫
    服務拆分的同時還要考慮到存儲數據庫也要獨立,當多個服務直接讀寫數據庫中同一張表時,對這些表作任何改動都須要協調這些相關服務的部署。這一點違背了服務相互獨立這一原則。共享的數據存儲很容易不經意間形成耦合。每一個服務須要有本身的私有數據。好比訂單表被訂單服務和商品服務所共享,商品服務單獨作統計並不知道本身一天多少商品被賣出,不知道哪些數據由本服務產生的,就沒法進行技術產品規劃,對錶結構的修改也要通知多個服務,這是所不能容忍的。每一個服務須要本身的數據庫,但這些數據庫可共置在一臺共享的數據服務器上,數據庫私有的重點在於不該讓服務知道其餘服務底層數據庫的存在。可用一臺共享數據服務器先開始開發,之後若是數據量和併發量變大,服務器能夠進行隔離。服務器隔離後,只要更改配置便可將不一樣服務的數據庫隔離起來。

  • 微服務之間肯定服務邊界,經過共享模型創建聯繫
    每一個微服務都具有本身的業務能力,那麼服務之間交互的部分便是服務邊界; 肯定服務邊界也是一個難題,須要對本身的產品和業務有足夠的瞭解才能肯定最天然的服務邊界肯定服務邊界堅持的原則是要高內聚弱耦合,弱耦合就是一個服務與其餘服務的任何通訊都應經過公開暴露的接口(API、事件等)實現,這些接口須要妥善設計以隱藏內部細節。這樣咱們的服務之間保持獨立,在將來咱們能夠輕鬆重構,高內聚力就是密切相關的多個功能應儘可能包含在同一個服務中,這樣可將服務之間的干擾降至最低。
    2.數據一致性

    在單體架構中,咱們經過數據庫事務完成的操做 放在分佈式微服務架構下沒法完成了,由於實例被部署不一樣服務器上,好比訂單服務進行下單操做,下單操做和扣減庫存應該放在同一個事務中,在微服務架構下,下單操做和扣庫存操做被分佈在不一樣服務器上,就須要進行分佈式事務操做,而分佈式事務具備延遲較高、nosql數據庫不支持等缺點。這些缺點致使分佈式事務沒法應用到微服務中在微服務場景下,咱們一般使用最終一致性來代替強一致性。

  • 可靠性事件模式
  • 補償模式-sagas模式

3.服務通訊

  • 通訊技術方案: RPC vs REST vs 異步消息
    • RPC、REST API、異步消息,異步消息咱們能夠藉助一些消息隊列框架來實現好比kafka、rrabbitMQ,那如今咱們說下rpc和使用http協議的相似REST API之間 如何選擇,TTP的好處是方便調試、跨語言、門檻低、普遍接受,一樣缺點是協議文檔很差維護,協議較爲繁瑣,性能較TCP要差是http協議的不足。
      Rpc通訊一般基於Tcp,經常使用的技術選型是thrift、grpc、dubbo,像thrift、grpc須要定義idl文件,經過idl文件來生成java代碼,經過rpc的好處是IDE友好,有代碼提示,協議維護在代碼中,傳參和響應結果都經過代碼能夠知道。但同時也有缺點好比不少rpc方案不知道跨語言,所支持的語言有限,須要定義和維護idl文件,且有必定的學習成本,另外rpc不容易方便的調試和測試
  • 服務註冊和發現
    • 在服務實例變化不定的環境中,用硬編碼指定IP地址的方式是行不通的,須要經過某種發現機制讓服務能相互查找。這就須要咱們將咱們的服務信息註冊到一個分佈式存儲中,這些服務信息就叫作服務註冊表服務註冊表能夠做爲信息的權威來源。其中包含有關可用服務的信息,以及服務網絡位置好比ip、端口號這些信息。那借助什麼組件進行實現呢,通常有Eureka和zookeeper,除此以外咱們還能夠藉助etcd,consul,redis這些。
  • 負載均衡
    • 有了註冊發現功能,客戶端經過服務註冊表發現實例清單並決定要鏈接哪一個實例,在客戶端作負載均衡,基於客戶端作負載均衡相比服務端負載均衡有諸多好處:首先節省了硬件均衡設備,減小了運維成本,其次能夠實現多種負載均衡策略好比響應感知的負載均衡策略。

4.服務網關

  • API Gateway
    咱們的微服務若是和終端用戶交互,勢必要考慮身份認證、安全防護等這些方面,若是每一個微服務都與終端用戶打交道,那麼這些方面代碼須要拷貝多份植入到每一個微服務業務代碼中。這形成了業務代碼和身份認證代碼的耦合,下降了代碼的複用性。這就須要在網絡邊界實現一個服務網關(這裏的網絡邊界能夠認爲是內網和外網之間的邊界),將身份認證,安全防護,流量控制這些功能放到服務網關中,向業務服務屏蔽網絡邊界服務的細節,使得業務服務專一於業務邏輯的開發維護和測試。
  • 爲前端服務的後端(Backends For Forntends)
    服務網關能夠根據終端產品形態來劃分,好比公共API,桌面客戶端,移動客戶端分別對應一個服務網關,而服務網關能夠是API Gateway只輸出api,或者是爲前端服務的後端,這裏的爲前端服務的後端,好比未來自多個服務的數據聚合到一塊兒返回給前端。
  • 身份認證、路由服務、限流防刷、日誌統計

5.高可觀察

  • 健康檢測、集中監控
    • 每一個服務和使用的組件都有提供健康檢測機制,使得咱們能夠及時發現異常的節點,而後作出判斷和調整,將全部的監控指標進行聚合輸出可視化圖表和界面幫助咱們快速直觀發現問題。
  • 日誌聚合及檢索
    • 好比在電商app咱們發現沒法進行下單操做,在分佈式架構下,日誌散落在多個服務多個服務器中,咱們不知道錯誤日誌打在哪臺服務器上,若是每臺服務器去登錄去看是極其低效的。這要求咱們作到日誌格式標準化,並經過一些手段聚合到一塊兒進行檢索查詢。同時可跨越全部服務、特定的某個服務,或服務的某個實例搜索日誌;將日誌發送至集中化日誌系統所用的代碼可包含在共享庫中或經過代碼腳手架提供。
  • 分佈式追蹤
    • 在微服務架構場景中,一個客戶端發起的請求要通過多個服務的調用最終聚合數據結構返回給客戶端,但咱們不知道這個請求不知道通過哪些服務,調用哪一個服務出現了問題,每一個服務的輸入輸出是什麼,這給咱們定位問題帶來了困擾,除此之外,若是一個請求耗時較長,咱們不知道到底哪一個服務耗時最長,好有針對性的性能優化。隨着架構的演進,咱們在架構設計規劃時須要知道 服務之間的依賴關係,這有須要什麼技術來實現呢,這就是咱們要介紹的分佈式追蹤,分佈式追蹤藉助關聯id,在請求源頭建立這個關聯id,而且在服務間進行透傳,最終將關聯id等信息聚合到一塊兒進行查詢分析。

6.可靠性

在講單體的過程當中,咱們講到,一個業務模塊的內存泄露會致使整個進程退出。 在微服務場景下,若是一個服務出現內存泄露是不會影響 沒有依賴關係的服務的。 可是卻能夠由於該異常服務的僵死或不可用形成上游服務線程hang住,進而產生級聯效應,故障進一步向上游傳播。

  • 流量控制,超時控制
    • 可靠性技術經過流量的控制和超時控制,保證服務消費者不被下游服務拖慢,及時對業務線程循環複用。
  • 艙壁隔離,熔斷機制
    • 熔斷是指服務調用出錯次數在必定時間達到必定數量,自動關閉對該服務的調用開關,改成返回錯誤或者將請求轉交給降級方法,下降資源耗盡的風險,當服務不可用時,做爲服務消費者應該對接口方法編寫一個降級方法。
  • 服務降級, 冪等重試
    • 因爲服務間通訊是經過網絡傳輸的,網絡異常和網絡分區故障 就會常常出現,咱們遇到這種狀況能夠進行調用重試,重試時要注意兩點,一個是接口必須是冪等的,不管運行一次或屢次,最終結果必須相同。冪等性保證了重試不會產生負面影響。在重試過程時休眠時間應該是指數增加的,不然會產生驚羣效應,好比:故障後的服務恢復上線後,若是有大量其餘服務正在同一個重試窗口內重試,此時很容易給系統形成巨大壓力。

提煉代碼腳手架

除了開發業務邏輯,咱們還須要搭建一套微服務工程可用框架,這樣是是爲了加快團隊工做效率,微服務解決方案保持統一,加強代碼複用性,統一進行優化,微服務要解決的問題很是多,因此抽取服務代碼模板是有必要的,它包括服務註冊發現、服務通訊、監控、日誌、異常處理等等。

從單體遷移到微服務

  • 什麼時候遷移到微服務上來
    講了這麼多微服務的好處,咱們是否是能夠今後拋棄單體架構,一切項目直接投向微服務的懷抱了呢? Martin Fowler在MicroservicePremium一文說 "don't even consider microservices unless you have a system that's too complex to manage as a monolith",翻譯過來就是若是你的系統不到足夠複雜的程度不要考慮使用微服務。

圖片描述
咱們先看這張圖,這張圖是從一篇外文中摘出來的,連接已經編輯在圖片中; 綠線表明單體架構,藍線表明微服務架構,X軸表明複雜度,Y軸表明生產效率。

當業務不復雜,團隊規模不大的時候,單塊架構比微服務架構具備更高的生產率(productivity),由於創建微服務架構須要額外的開銷來支持和管理微服務, 從而下降生產率;可是隨着業務複雜性的增長和團隊規模的擴大,單塊架構比微服務架構的生產率降低更趨明顯, 當複雜性達到一個臨界點,微服務架構的生產率會優於單塊架構, 由於微服務的鬆散耦合自治特性減緩了生產率的降低趨勢。

因此咱們在作項目時,必定要根據本身的團隊和業務複雜性來判斷,什麼時候應用微服務。 以個人經驗給你們的建議時,一個全新項目在1-3團隊時,能夠先拆分紅一個API 網關和一個集合全部業務的後端服務,API網關關注鑑權和路由、處理部分失敗;後端服務要劃分好業務模塊;項目初期,因爲流量很少,能夠沒必要添加流量控制和斷路器等組件。

同時即便向微服務架構遷移以後,拆分的粒度也要由粗到細演進式發展,必定要根據本身的業務規模複雜性和團隊規模來定。

  • 如何遷移到微服務架構上來

一個策略是:不要大規模(big bang)重寫代碼(只有當你承擔重建一套全新基於微服務的應用時候能夠採用重寫這種方法)。重寫代碼聽起來很不錯,但實際上充滿了風險最終可能會失敗,就如Martin Fowler所說:「the only thing a Big Bang rewrite guarantees is a Big Bang!」

相反,應該採起逐步遷移單體式應用的策略,經過逐步生成微服務新應用,與舊的單體式應用集成,隨着時間推移,單體式應用在整個架構中比例逐漸降低直到消失或者成爲微服務架構一部分。這個策略有點像在高速路上限速到70邁對車作維護,儘管有挑戰,可是比起重寫的風險小不少。

Martin Fowler將這種現代化策略成爲絞殺應用,名字來源於雨林中的絞殺藤(strangler vine),也叫絞殺榕(strangler fig)。絞殺藤爲了爬到森林頂端都要纏繞着大叔生長,一段時間後,樹死了,留下樹形藤。這種應用也使用同一種模式,圍繞着傳統應用開發了新型微服務應用,傳統應用會漸漸退出舞臺。

SOA VS 微服務

SOA和微服務的對比是一個老生常談的話題,我認爲二者最大的不一樣是提出時所處的技術背景和環境。

SOA的出現實際上是爲了解決歷史問題:企業在信息化的過程當中會有各類各樣互相隔離的系統,須要有一種機制將他們整合起來,因此纔會有ESB的出現。一樣的,也成了SOA初期的服務是很大的概念,一般指定的一個能夠獨立運做的系統。

微服務沒有歷史包袱,服務的尺寸一般不會太大,從服務粒度來說,SOA更像是單體的簡單組合,而微服務是粒度更細小的服務,其次數據拆分SOA傾向於共享數據庫,微服務一個服務對應一個數據庫。

微服務知識全景圖

微服務知識全景圖會在課程《Java深刻微服務原理改造房產銷售平臺》中介紹和實踐。
圖片描述

相關文章
相關標籤/搜索