1. 程式人生 > 程式設計 >SpringBoot單體服務是如何搭建成SpringCloud微服務

SpringBoot單體服務是如何搭建成SpringCloud微服務

本文是讀了《Spring微服務實戰》一書後的總結,介紹如何將傳統應用程式一步步構建成分散式微服務架構。

傳統應用程式是怎樣的呢?

​ 傳統應用程式是高度耦合的,開發者將業務邏輯,模組之間的呼叫硬編碼在程式碼之中,使各個模組之間相互依賴。

​ 在軟體規模擴大之後,通常遭遇到這樣一種情況:針對某一需求的修改可能會對其他依賴此需求的部分產生影響,從而牽一髮而動全身,同時每一修改都需要重新構建、編譯和部署。雖然使用Spring框架可以降低耦合,但對於大型,需求複雜的多個開發團隊同時開發的軟體專案來說,依然捉襟見肘。

​ 不安全的。一般軟體都將相同資料模型放在同一資料庫中,即使是不同的業務資料。這會造成單個模組的訪問許可權過大,每個業務模組應當只負責並且只有當前所涉及業務內的資料訪問許可權。

​ 伸縮性是有限的,不靈活的。傳統應用服務在處理高併發,多使用者時,一般採用提升機器效能來提升處理速度,這樣的解決方案在短期的效果是很顯著的,但提升效能和機器的成本比例是不划算的,並且機器的效能是有瓶頸的;另一種解決方案是用多臺效能一般的機器搭建服務叢集,這種方案對服務程式是有要求的,應用程式必須是無狀態的,也就是對於業務,使用者等相關的資料不能再本地快取,服務可以在任何超時時被殺死和替換,而不用擔心一個服務例項的丟失會導致資料丟失。

​ 由於不均勻的容量需求,使用者對某一快業務的訪問量是不同的,在以上搭建叢集的時候,水平擴充套件了所有的業務模組以滿足某一部分功能的處理能力提升。在伺服器中的任何資源都是十分寶貴的,這造成了對服務資源的浪費。

面向服務的分散式架構

​ 針對以上問題,軟體不得不進行重構,採用面向服務的分散式架構(SOA)。合理拆分軟體的不同業務模組,是模組之間協調工作,如圖結構:

圖1

​ 服務消費者通過DNS解析地址到主負載均衡器,主負載均衡器查詢路由表獲取服務例項位置,呼叫服務。輔助負載均衡器會處於空閒狀態,僅在主負載均衡器中斷的情況下才會切換故障處理負載。

統一配置

​ 對於系統中的每個服務例項,開發人員必須在伺服器上對應用程式進行配置,這可能適用於少量服務,在對包含數百個服務的應用程式配置時,不僅是配置的重複程度,開發人員無法保證每個服務的服務例項的執行環境以及配置屬性是完全相同的。

​ 可以採用統一配置伺服器控制所有的伺服器配置(Spring Cloud Config)。這樣在服務啟動時就可以都向配置伺服器拉取當前服務的配置資訊,而配置服務可以統一管理所有服務配置,並對它進行版本化控制。

圖2

服務發現

​ 由圖1結構可以發現一些弊端:

  • 服務消費者會高度依賴主負載均衡器,主負載均衡器是應用程式基礎設施中的集中式阻塞點,也是整個基礎設施的單點故障,如果它出現故障,那麼依賴它的每個應用程式都會出現故障。
  • 有限的伸縮性依然存在。在服務集中到單個負載均衡器叢集的情況下,跨多個伺服器水平伸縮的能力有限,一般情況下只能使用單個伺服器處理負載,而輔助負載均衡器會處於空閒狀態,僅在主負載均衡器中斷的情況下,才會切換故障處理負載。
  • 通過手動配置新增路由資訊,不能快速對服務進行註冊和登出。手動配置服務消費者的請求和服務路徑的對映關係,這可能會造成由於部署或修改時的小失誤而導致系統迅速崩潰。

​ 針對以上問題,可以引入服務發現引擎。通過服務發現叢集實現高可用。服務可以在服務發現服務中進行註冊,並且由於叢集的特性,如果一個節點變得不可用,叢集中的其他節點可以接管工作。

​ 根據服務發現的實現機制不同,叢集的工作策略也不同。

​ 例如:

​ Eureka服務發現引擎側重於提供高可用的服務發現服務。服務消費者本地儲存多個服務發現叢集節點,當叢集中的某一節點崩潰時,將切換其他節點進行訪問。

​ Zookeeper則側重於提供統一的可用健康的服務例項,採用主/從模式。叢集中有一個leader節點,其他節點從leader節點同步資料,當leader節點崩潰時,其他節點能夠進行投票選舉出新的leader節點對外提供服務,但在選舉期間就會出現服務發現無法提供服務的情況。Zookeeper叢集的特點是在保證一半以上的節點存活時能夠對完提供服務,並且一般叢集的主機數量為奇數。

圖3

​ 這樣服務的提供者對於服務消費者來說是位置透明的,服務消費者不需要關心服務提供者是誰,也不用管理服務提供者的健康狀態,服務發現服務會定期檢查所註冊服務的健康狀態,對於未能返回良好狀態的服務都將從服務例項池中刪除。

​ 但是使用這種方法依然很脆弱,服務消費者完全依賴於服務發現引擎來查詢呼叫服務。為服務消費者新增客戶端負載均衡可以在服務發現代理不可用時,客戶端依然能夠與服務提供者正常通訊,增加了整個系統的健壯性。

圖4

​ 當服務消費者呼叫服務時,首先會檢查本地快取中的服務位置資訊,如果沒有則會聯絡服務發現服務,獲取它的所有服務例項,並快取到本地。在獲得服務例項列表後,它會使用簡單的負載均衡演演算法,如“輪詢”,以確保服務呼叫分佈在多個服務例項之間。並且,客戶端會定期與服務發現服務聯絡重新整理服務例項快取。

​ 當某次呼叫服務失敗,客戶端會通知服務發現引擎刪除該例項,並從服務發現服務重新整理快取資料。

客戶端彈性模式(Hystrix)

​ 當服務例項徹底崩潰時,很容易檢測到服務死亡,應用程式可以繞過它,但當服務執行緩慢時,檢測服務效能,健康狀態不佳時是非常困難的。

​ 並且潛在的可能會由於它的執行緩慢將耗盡整個伺服器的資源,乃至拖垮整個服務系統。比如一個系統有A,B,C三個服務,關係如圖:

圖5

​ A 服務依賴B服務,B服務依賴C服務,C服務於資料庫連線。

​ 此時,如果由於某些原因導致C服務與資料庫通訊緩慢,那麼C服務的資料庫連線將會堵塞,而接下來請求新建的資料庫連線依然會堵塞,最終會導致資料庫連線池資源耗盡;而呼叫C服務的B服務的執行緒也將會堵塞,並且最終伺服器容器中的執行緒池也會迅速的消耗殆盡,最後A服務也將耗盡資源,因為他呼叫了B服務,而整個起點是因為C服務訪問資料庫緩慢。

斷路器

​ 我們可以在服務消費者與服務提供者之間加上斷路器,可以避免這種情況。

​ 當開啟一個請求時,斷路器將監視這個呼叫,如果呼叫時間太長,斷路器就會強制終止此次呼叫,如果呼叫失敗次數過多,那麼斷路器會採取快速失敗阻止將來呼叫。

後備模式

​ 我們還可以新增後備處理,在遠端呼叫失敗之後。採用執行替代的程式碼給使用者返回一個友好的結果而不是一個錯誤警告。有時候舊資料比沒有資料好。

艙壁模式

​ 為了降低一個緩慢的呼叫拖垮整個應用程式的風險,可以為每個服務呼叫單獨分配執行緒池,這樣即使服務響應緩慢,也只會耗盡當前執行緒池的資源,而不會影響其他的服務呼叫。

圖6

服務路由(Zuul)

​ 在分散式架構中某些關鍵的行為經常要跨多個服務呼叫,如安全,日誌記錄,使用者跟蹤等。為了實現他們。要求開發人員在所有服務中始終如一地強制這些特性,而不需要每個開發團隊構建自己的解決方案,儘管可以採用公共庫或自定義框架來實現這些功能,但會帶來一些影響,比如開發人員可能會忘記和遺漏編寫日誌或跟蹤,並且這會對所有服務造成依賴,在修改公共框架或新增功能時,必須重新編譯和部署所有的服務,這是一件非常糟糕的事情。

​ 對於日誌記錄此類事件,在服務中共同存在的稱為橫向關注點,還記得Spring中的切面、切點概念嗎。我們可以將這些橫切關注點抽象成一個獨立且作為應用程式中所有微服務呼叫的過濾器和路由器的服務,稱之為“服務閘道器”。將所有的呼叫服務放在服務閘道器的後面,客戶端只與服務閘道器通訊,由服務閘道器代理轉發客戶端呼叫。

​ 服務閘道器如此一來就控制住了微服務的所有流量入口,所有的橫切服務都可以在這實現,並且所有服務呼叫都對映到一個URL後面,在搭建微服務時可以將所有服務都放在內網之中,只暴露服務閘道器的外網呼叫埠,極大極高了服務的安全性。

​ 之前的訪問:

圖7

​ 加入服務閘道器:

圖8

​ 大家可能發現這樣服務閘道器不就成了單點故障和阻塞點了嗎?可以構建服務閘道器叢集,在多個服務閘道器例項前放置負載均衡器。在單個服務組前面,負載均衡器是很有用的,但放置在所有服務例項的前面將會成為瓶頸。部署服務閘道器時要保證其實無狀態的,輕量的,具有多個資料庫呼叫的複雜程式碼可能是閘道器中難以跟蹤的效能問題來源。

保護微服務(OAuth2)

​ 系統安全是非常重要的,對使用者身份的驗證與授權是保證資源不為惡意使用的手段之一,採用OAuth2構建驗證伺服器,對使用者和應用程式進行身份驗證和授權。

圖9

​ 使用者使用客戶端向驗證伺服器提供憑據,驗證伺服器需要驗證客戶端和使用者,成功後返回一個令牌,客戶端使用此令牌訪問受保護資源服務,受保護資源服務收到令牌後向驗證伺服器確認令牌的合法性,並獲取使用者基本資訊,如果受保護資源服務需要呼叫其他服務依然要提供客戶端傳來的令牌給被呼叫服務,被呼叫服務向驗證服務確認。

總結

​ 這裡只是本人對《Spring微服務實戰》書的閱讀總結,其中還有部分沒有總結到,比如還可以在微服務中引入訊息佇列,快取服務等,利用Zipkin進行分散式跟蹤,構建持續整合/持續交付(CI/CD)管道等。該書採用理論是實踐結合的方式講述SpringBoot服務如何一步一步搭建成微服務。作為讀者也對微服務的概念有了一個清晰的認識。並且在以後的開發過程中也會注意到書中所講的一些問題,總之收穫很大。

​ 最後一個完整的微服務架構圖:

圖10