1. 程式人生 > >走進Spring Cloud之一 微服務和SpringCloud

走進Spring Cloud之一 微服務和SpringCloud

走進Spring Cloud之一 微服務和SpringCloud

Monolithic架構(單體架構)

單體架構

開發web程式的最早期時間,最被廣泛使用的企業程式架構是將程式的伺服器端元件打包為單個單元。例如很多企業將Java應用程式打包為單個WAR完整執行。單體架構在很多小型的Web服務中很常見,因為其優點很明顯:

  • 服務量級較小,開發人員協調型不高,只需要少數開發配合。
  • 開發簡單直接,集中式管理, 基本不會重複開發。
  • 功能都在本地,沒有分散式的管理開銷和呼叫開銷。

但是隨著業務的衍生與擴充套件,服務量增加的時候,它的缺點也很明顯:

  • 開發效率低:所有的開發人員在一個專案改程式碼,耦合依賴性太高,提交程式碼相互等待,功能互相依賴。
  • 程式碼維護難:程式碼功能耦合在一起,新人不知道何從下手
  • 部署不靈活:構建時間長,任何小修改必須重新構建整個專案,這個過程往往很長
  • 穩定性不高:一個微不足道的小問題,可以導致整個應用掛掉,或者最常見為了更新一個小服務需要重新打包整個應用。
    -擴充套件性不夠:無法滿足高併發情況下的業務需求

微服務架構

微服務最早由Martin Fowler與James Lewis於2014年共同提出,微服務是指開發一個個(或一系列)單個小型的但有業務功能的服務,每個服務都有自己的處理和輕量通訊機制,可以部署在單個或多個伺服器上。微服務也指一種鬆耦合的、有一定的有界上下文的面向服務架構。也就是說,如果每個服務都要同時修改,那麼它們就不是微服務,因為它們緊耦合在一起;如果你需要掌握一個服務太多的上下文場景使用條件,那麼它就是一個有上下文邊界的服務,這個定義來自DDD領域驅動設計。

相對於單體架構和SOA,它的主要特點是元件化、鬆耦合、自治、去中心化,體現在以下幾個方面:

  • 一組小的服務 : 服務粒度要小,而每個服務是針對一個單一職責的業務能力的封裝,專注做好一件事情。
  • 獨立部署執行和擴充套件 :每個服務能夠獨立被部署並執行在一個程序內。這種執行和部署方式能夠賦予系統靈活的程式碼組織方式和釋出節奏,使得快速交付和應對變化成為可能。
  • 獨立開發和演化 :技術選型靈活,不受遺留系統技術約束。合適的業務問題選擇合適的技術可以獨立演化。服務與服務之間採取與語言無關的API進行整合。相對單體架構,微服務架構是更面向業務創新的一種架構模式。
  • 獨立團隊和自治 : 團隊對服務的整個生命週期負責,工作在獨立的上下文中,自己決策自己治理,而不需要統一的指揮中心。團隊和團隊之間通過鬆散的社群部落進行銜接。

我們可以看到整個微服務的思想就如我們現在面對資訊爆炸、知識爆炸是一樣的:通過解耦我們所做的事情,分而治之以減少不必要的損耗,使得整個複雜的系統和組織能夠快速的應對變化。

為什麼採用微服務呢?

“讓我們的系統儘可能快地響應變化” - Rebecca Parson

讓我們的系統儘可能快地去響應變化.其實幾十年來我們一直在嘗試解決這個問題。如果一定要在前面加個限制的話,那就是低成本的快速響應變化。上世紀90年代Kent Beck提出要擁抱變化,在同期出現了諸多輕量級開發方法(諸如 XP、Scrum);2001年敏捷宣言誕生,之後又出現了精益、看板等新的管理方式。如果說,這些是為了儘快的響應變化,在軟體開發流程和實踐方面提出的解決方案,那麼微服務架構就是在軟體技術和架構層面提出的應對之道。

服務註冊、發現、負載均衡和健康檢查

和單體 (Monolithic) 架構的結構不同,微服務架構是由一系列職責單一的細粒度服務構成的分散式網狀結構,服務之間通過輕量機制進行通訊,這時候必然引入一個服務註冊發現問題,也就是說服務提供方要註冊通告服務地址,服務的呼叫方要能發現目標服務,同時服務提供方一般以叢集方式提供服務,也就引入了負載均衡和健康檢查問題。根據負載均衡 LB 所在位置的不同,目前主要的服務註冊、發現和負載均衡方案有三種:

集中式 LB 方案

集中式 LB 方案,在服務消費者和服務提供者之間有一個獨立的 LoadBalance(後面簡稱LB),LB 通常是專門的硬體裝置如 F5,或者基於軟體如 LVS,HAproxy 等實現。LB 上有所有服務的地址對映表,通常由運維配置註冊,當服務消費方呼叫某個目標服務時,它向 LB 發起請求,由 LB 以某種策略(比如 Round-Robin)做負載均衡後將請求轉發到目標服務。LB 一般具備健康檢查能力,能自動摘除不健康的服務例項。服務消費方如何發現 LB 呢?通常的做法是通過 DNS,運維人員為服務配置一個 DNS 域名,這個域名指向 LB。

集中式LB

集中式LB方案實現簡單,在LB上也容易做集中式的訪問控制,這一方案目前還是業界主流。集中式LB的主要問題是單點問題,所有服務呼叫流量都經過LB,當服務數量和呼叫量大的時候,LB容易成為瓶頸,且一旦LB發生故障對整個系統的影響是災難性的。另外,LB在服務消費方和服務提供方之間增加了一跳(hop),有一定效能開銷。

程序內LB方案

針對集中式LB的不足,程序內LB方案將LB的功能以庫的形式整合到服務消費方程序裡頭,該方案也被稱為軟負載(Soft Load Balancing)或者客戶端負載方案,如圖展示了這種方案的工作原理。這一方案需要一個服務登錄檔(Service Registry)配合支援服務自注冊和自發現,服務提供方啟動時,首先將服務地址註冊到服務登錄檔(同時定期報心跳到服務登錄檔以表明服務的存活狀態,相當於健康檢查),服務消費方要訪問某個服務時,它通過內建的LB元件向服務登錄檔查詢(同時快取並定期重新整理)目標服務地址列表,然後以某種負載均衡策略選擇一個目標服務地址,最後向目標服務發起請求。這一方案對服務登錄檔的可用性(Availability)要求很高,一般採用能滿足高可用分散式一致的元件(例如Zookeeper, Consul, Etcd等)來實現。

程序內LB方案

程序內LB方案是一種分散式方案,LB和服務發現能力被分散到每一個服務消費者的程序內部,同時服務消費方和服務提供方之間是直接呼叫,沒有額外開銷,效能比較好。但是,該方案以客戶庫(Client Library)的方式整合到服務呼叫方程序裡頭,如果企業內有多種不同的語言棧,就要配合開發多種不同的客戶端,有一定的研發和維護成本。另外,一旦客戶端跟隨服務呼叫方釋出到生產環境中,後續如果要對客戶庫進行升級,勢必要求服務呼叫方修改程式碼並重新發布,所以該方案的升級推廣有不小的阻力。

程序內LB的案例是Netflix的開源服務框架,對應的元件分別是:Eureka服務登錄檔,Karyon服務端框架支援服務自注冊和健康檢查,Ribbon客戶端框架支援服務自發現和軟路由。另外,阿里開源的服務框架Dubbo也是採用類似機制。

主機獨立LB程序方案

主機獨立LB程序方案是針對第二種方案的不足而提出的一種折中方案,原理和第二種方案基本類似,不同之處是,他將LB和服務發現功能從程序內移出來,變成主機上的一個獨立程序,主機上的一個或者多個服務要訪問目標服務時,他們都通過同一主機上的獨立LB程序做服務發現和負載均衡,見下圖
程序內LB方案

該方案也是一種分散式方案,沒有單點問題,一個LB程序掛了隻影響該主機上的服務呼叫方,服務呼叫方和LB之間是程序內呼叫,效能好,同時,該方案還簡化了服務呼叫方,不需要為不同語言開發客戶庫,LB的升級不需要服務呼叫方改程式碼。該方案的不足是部署較複雜,環節多,出錯除錯排查問題不方便。

該方案的典型案例是Airbnb的SmartStack服務發現框架,對應元件分別是:Zookeeper作為服務登錄檔,Nerve獨立程序負責服務註冊和健康檢查,Synapse/HAproxy獨立程序負責服務發現和負載均衡。Google最新推出的基於容器的PaaS平臺Kubernetes,其內部服務發現採用類似的機制。

服務前端路由

微服務除了內部相互之間呼叫和通訊之外,最終要以某種方式暴露出去,才能讓外界系統(例如客戶的瀏覽器、移動裝置等等)訪問到,這就涉及服務的前端路由,對應的元件是服務閘道器(Service Gateway),見下圖,閘道器是連線企業內部和外部系統的一道門,有如下關鍵作用:

  • 服務反向路由,閘道器要負責將外部請求反向路由到內部具體的微服務,這樣雖然企業內部是複雜的分散式微服務結構,但是外部系統從閘道器上看到的就像是一個統一的完整服務,閘道器遮蔽了後臺服務的複雜性,同時也遮蔽了後臺服務的升級和變化。

  • 安全認證和防爬蟲,所有外部請求必須經過閘道器,閘道器可以集中對訪問進行安全控制,比如使用者認證和授權,同時還可以分析訪問模式實現防爬蟲功能,閘道器是連線企業內外系統的安全之門。

  • 限流和容錯,在流量高峰期,閘道器可以限制流量,保護後臺系統不被大流量沖垮,在內部系統出現故障時,閘道器可以集中做容錯,保持外部良好的使用者體驗。

  • 監控,閘道器可以集中監控訪問量,呼叫延遲,錯誤計數和訪問模式,為後端的效能優化或者擴容提供資料支援。

  • 日誌,閘道器可以收集所有的訪問日誌,進入後臺系統做進一步分析。

Fig 4, 服務閘道器

除以上基本能力外,閘道器還可以實現線上引流,線上壓測,線上除錯(Surgical debugging),金絲雀測試(Canary Testing),資料中心雙活(Active-Active HA)等高階功能。

閘道器通常工作在7層,有一定的計算邏輯,一般以叢集方式部署,前置LB進行負載均衡。

開源的閘道器元件有Netflix的Zuul,特點是動態可熱部署的過濾器(filter)機制,其它如HAproxy,Nginx等都可以擴充套件作為閘道器使用。

在介紹過服務登錄檔和閘道器等元件之後,我們可以通過一個簡化的微服務架構圖來更加直觀地展示整個微服務體系內的服務註冊發現和路由機制,該圖假定採用程序內LB服務發現和負載均衡機制。在下圖的微服務架構中,服務簡化為兩層,後端通用服務(也稱中間層服務Middle Tier Service)和前端服務(也稱邊緣服務Edge Service,前端服務的作用是對後端服務做必要的聚合和裁剪後暴露給外部不同的裝置,如PC,Pad或者Phone)。後端服務啟動時會將地址資訊註冊到服務登錄檔,前端服務通過查詢服務登錄檔就可以發現然後呼叫後端服務;前端服務啟動時也會將地址資訊註冊到服務登錄檔,這樣閘道器通過查詢服務登錄檔就可以將請求路由到目標前端服務,這樣整個微服務體系的服務自注冊自發現和軟路由就通過服務登錄檔和閘道器串聯起來了。如果以面向物件設計模式的視角來看,閘道器類似Proxy代理或者Façade門面模式,而服務登錄檔和服務自注冊自發現類似IoC依賴注入模式,微服務可以理解為基於閘道器代理和登錄檔IoC構建的分散式系統。

Fig 5, 簡化的微服務架構圖

服務容錯

當企業微服務化以後,服務之間會有錯綜複雜的依賴關係,例如,一個前端請求一般會依賴於多個後端服務,技術上稱為1 -> N扇出(見下圖1)。在實際生產環境中,服務往往不是百分百可靠,服務可能會出錯或者產生延遲,如果一個應用不能對其依賴的故障進行容錯和隔離,那麼該應用本身就處在被拖垮的風險中。在一個高流量的網站中,某個單一後端一旦發生延遲,可能在數秒內導致所有應用資源(執行緒,佇列等)被耗盡,造成所謂的雪崩效應(Cascading Failure,見下圖2),嚴重時可致整個網站癱瘓。

服務多依賴:
Fig 6, 服務依賴

高峰期單個服務延遲致雪崩效應:
Fig 7, 高峰期單個服務延遲致雪崩效應

經過多年的探索和實踐,業界在分散式服務容錯一塊探索出了一套有效的容錯模式和最佳實踐,主要包括:

Fig 8, 彈性電路保護狀態圖

  • 電路熔斷器模式(Circuit Breaker Patten), 該模式的原理類似於家裡的電路熔斷器,如果家裡的電路發生短路,熔斷器能夠主動熔斷電路,以避免災難性損失。在分散式系統中應用電路熔斷器模式後,當目標服務慢或者大量超時,呼叫方能夠主動熔斷,以防止服務被進一步拖垮;如果情況又好轉了,電路又能自動恢復,這就是所謂的彈性容錯,系統有自恢復能力。上圖是一個典型的具備彈性恢復能力的電路保護器狀態圖,正常狀態下,電路處於關閉狀態(Closed),如果呼叫持續出錯或者超時,電路被開啟進入熔斷狀態(Open),後續一段時間內的所有呼叫都會被拒絕(Fail Fast),一段時間以後,保護器會嘗試進入半熔斷狀態(Half-Open),允許少量請求進來嘗試,如果呼叫仍然失敗,則回到熔斷狀態,如果呼叫成功,則回到電路閉合狀態。

  • 艙壁隔離模式(Bulkhead Isolation Pattern),顧名思義,該模式像艙壁一樣對資源或失敗單元進行隔離,如果一個船艙破了進水,只損失一個船艙,其它船艙可以不受影響 。執行緒隔離(Thread Isolation)就是艙壁隔離模式的一個例子,假定一個應用程式A呼叫了Svc1/Svc2/Svc3三個服務,且部署A的容器一共有120個工作執行緒,採用執行緒隔離機制,可以給對Svc1/Svc2/Svc3的呼叫各分配40個執行緒,當Svc2慢了,給Svc2分配的40個執行緒因慢而阻塞並最終耗盡,執行緒隔離可以保證給Svc1/Svc3分配的80個執行緒可以不受影響,如果沒有這種隔離機制,當Svc2慢的時候,120個工作執行緒會很快全部被對Svc2的呼叫吃光,整個應用程式會全部慢下來。

  • 限流(Rate Limiting/Load Shedder),服務總有容量限制,沒有限流機制的服務很容易在突發流量(秒殺,雙十一)時被沖垮。限流通常指對服務限定併發訪問量,比如單位時間只允許100個併發呼叫,對超過這個限制的請求要拒絕並回退。

  • 回退(fallback),在熔斷或者限流發生的時候,應用程式的後續處理邏輯是什麼?回退是系統的彈性恢復能力,常見的處理策略有,直接丟擲異常,也稱快速失敗(Fail Fast),也可以返回空值或預設值,還可以返回備份資料,如果主服務熔斷了,可以從備份服務獲取資料。

Netflix將上述容錯模式和最佳實踐整合到一個稱為Hystrix的開源元件中,凡是需要容錯的依賴點(服務,快取,資料庫訪問等),開發人員只需要將呼叫封裝在Hystrix Command裡頭,則相關呼叫就自動置於Hystrix的彈性容錯保護之下。Hystrix元件已經在Netflix經過多年運維驗證,是Netflix微服務平臺穩定性和彈性的基石,正逐漸被社群接受為標準容錯元件。

服務框架

微服務化以後,為了讓業務開發人員專注於業務邏輯實現,避免冗餘和重複勞動,規範研發提升效率,必然要將一些公共關注點推到框架層面。下圖服務框架主要封裝公共關注點邏輯,包括:

Fig 9, 服務框架

  1. 服務註冊、發現、負載均衡和健康檢查,假定採用程序內LB方案,那麼服務自注冊一般統一做在伺服器端框架中,健康檢查邏輯由具體業務服務定製,框架層提供呼叫健康檢查邏輯的機制,服務發現和負載均衡則整合在服務客戶端框架中。

  2. 監控日誌,框架一方面要記錄重要的框架層日誌、metrics和呼叫鏈資料,還要將日誌、metrics等介面暴露出來,讓業務層能根據需要記錄業務日誌資料。在執行環境中,所有日誌資料一般集中落地到企業後臺日誌系統,做進一步分析和處理。

  3. REST/RPC和序列化,框架層要支援將業務邏輯以HTTP/REST或者RPC方式暴露出來,HTTP/REST是當前主流API暴露方式,在效能要求高的場合則可採用Binary/RPC方式。針對當前多樣化的裝置型別(瀏覽器、普通PC、無線裝置等),框架層要支援可定製的序列化機制,例如,對瀏覽器,框架支援輸出Ajax友好的JSON訊息格式,而對無線裝置上的Native App,框架支援輸出效能高的Binary訊息格式。

  4. 配置,除了支援普通配置檔案方式的配置,框架層還可整合動態執行時配置,能夠在執行時針對不同環境動態調整服務的引數和配置。

  5. 限流和容錯,框架整合限流容錯元件,能夠在執行時自動限流和容錯,保護服務,如果進一步和動態配置相結合,還可以實現動態限流和熔斷。

  6. 管理介面,框架整合管理介面,一方面可以線上檢視框架和服務內部狀態,同時還可以動態調整內部狀態,對除錯、監控和管理能提供快速反饋。Spring Boot微框架的Actuator模組就是一個強大的管理介面。

  7. 統一錯誤處理,對於框架層和服務的內部異常,如果框架層能夠統一處理並記錄日誌,對服務監控和快速問題定位有很大幫助。

  8. 安全,安全和訪問控制邏輯可以在框架層統一進行封裝,可做成外掛形式,具體業務服務根據需要載入相關安全外掛。

  9. 文件自動生成,文件的書寫和同步一直是一個痛點,框架層如果能支援文件的自動生成和同步,會給使用API的開發和測試人員帶來極大便利。Swagger是一種流行Restful API的文件方案。

當前業界比較成熟的微服務框架有Netflix的Karyon/Ribbon,Spring的Spring Boot/Cloud,阿里的Dubbo等。

執行期配置管理

服務一般有很多依賴配置,例如訪問資料庫有連線字串配置,連線池大小和連線超時配置,這些配置在不同環境(開發/測試/生產)一般不同,比如生產環境需要配連線池,而開發測試環境可能不配,另外有些引數配置在執行期可能還要動態調整,例如,執行時根據流量狀況動態調整限流和熔斷閥值。目前比較常見的做法是搭建一個執行時配置中心支援微服務的動態配置,簡化架構如下圖
Fig 10, 服務配置中心

動態配置存放在集中的配置伺服器上,使用者通過管理介面配置和調整服務配置,具體服務通過定期拉(Scheduled Pull)的方式或者伺服器推(Server-side Push)的方式更新動態配置,拉方式比較可靠,但會有延遲同時有無效網路開銷(假設配置不常更新),伺服器推方式能及時更新配置,但是實現較複雜,一般在服務和配置伺服器之間要建立長連線。配置中心還要解決配置的版本控制和審計問題,對於大規模服務化環境,配置中心還要考慮分散式和高可用問題。

配置中心比較成熟的開源方案有百度的Disconf,360的QConf,Spring的Cloud Config和阿里的Diamond等。

Netflix的微服務框架

Netflix是一家成功實踐微服務架構的網際網路公司,幾年前,Netflix就把它的幾乎整個微服務框架棧開源貢獻給了社群,這些框架和元件包括:

  1. Eureka: 服務註冊發現框架
  2. Zuul: 服務閘道器
  3. Karyon: 服務端框架
  4. Ribbon: 客戶端框架
  5. Hystrix: 服務容錯元件
  6. Archaius: 服務配置元件
  7. Servo: Metrics元件
  8. Blitz4j: 日誌元件

下圖展示了基於這些元件構建的一個微服務框架體系,來自recipes-rss。

Fig 11, 基於Netflix開源元件的微服務框架

Netflix的開源框架元件已經在Netflix的大規模分散式微服務環境中經過多年的生產實戰驗證,正逐步被社群接受為構造微服務框架的標準組件。Pivotal去年推出的Spring Cloud開源產品,主要是基於對Netflix開源元件的進一步封裝,方便Spring開發人員構建微服務基礎框架。對於一些打算構建微服務框架體系的公司來說,充分利用或參考借鑑Netflix的開源微服務元件(或Spring Cloud),在此基礎上進行必要的企業定製,無疑是通向微服務架構的捷徑。

Spring Cloud

Spring Cloud為開發人員提供了快速構建分散式系統中一些常見模式的工具(例如配置管理,服務發現註冊、微代理、訊息匯流排、負載均衡、斷路器、資料監控等)。分散式系統的協調導致了樣板模式, 使用Spring Cloud開發人員可以快速地支援實現這些模式的服務和應用程式。他們將在任何分散式環境中執行良好,包括開發人員自己的膝上型電腦,裸機資料中心,以及Cloud Foundry等託管平臺。

特性

Spring Cloud專注於提供良好的開箱即用經驗的典型用例和可擴充套件性機制覆蓋。

  • 分散式/版本化配置
  • 服務註冊和發現
  • 路由
  • service - to - service呼叫
  • 負載均衡
  • 斷路器
  • 分散式訊息傳遞

家族

1
2
3
4
6
6

Spring Cloud Netflix

Spring Cloud NetflixSpring Boot 應用程式利用autoconfiguration繫結Spring上下文環境的方式或者其他的Spring系程式提供了 Netflix OSS 整合 。通過一些簡單的註解,我們可以快速的在我們的應用程式內部啟用和配置常用屬性從而利用強悍的Netflix 元件構建我的分散式系統。 Netflix提供的元件包括服務發現(Eureka)、斷路器(Hystrix)、智慧路由(Zuul)和客戶端負載平衡(Ribbon)。

Spring Cloud Netflix 特徵:

  • 服務發現: Eureka 例項會被註冊並且客戶端可以通過Spring託管的beans發現Eureka 例項。

  • 服務發現: 內嵌的 Eureka server 使用簡單的Java申明式註解實現。

  • 斷路器: Hystrix 客戶端可以使用一些簡單註解裝飾實現。

  • 斷路器: 內嵌的 Hystrix 控制檯使用簡單的Java申明式註解實現。

  • 宣告性REST客戶端: Feign 用JAX-RS或Spring MVC註解建立可以實現的動態介面。

  • 客戶端負載均衡 :Ribbon

  • 外部配置: Spring 環境 和 Archaius (使用Spring Boot約定啟用Netflix元件的本地配置)的橋樑。

  • 路由器和過濾器: Zuul過濾器的自動重新定位,以及反向代理建立的配置方法上的簡單約定

深度鳴謝

實施微服務,我們需要哪些基礎框架?