基於 SpringCloud 的微服務治理架構落地實踐

一 概述

關於微服務的介紹目前已經有很多文章做了介紹,本文不再對微服務的概念再做進一步闡述,重點將介紹微服務架構具體開發運維方面的經驗總結,側重於落地實踐。

目前業界比較熱門的微服務開發框架是SpringCloud和dubbo,由於前期一些項目已經使用了SpringBoot進行快速開發,自然就平滑地升級到SpringCloud進行微服務實踐。另外,按照微服務不斷演進的思路,我們首先對非核心業務和新業務進行了重構,不斷積累微服務開發運維經驗,待後續時機成熟,再對核心業務進行微服務重構治理。

二 業務梳理

2.1 業務梳理原則

和大部分企業實施微服務流程一樣,首先就是確定微服務重構的原則和目標。由於證券行業的自身特點,確定瞭如下幾個原則:

1. 安全放在首位。安全是第一位的非功能性需求,在設計之初,就應該考慮到,涉及服務器、容器、應用、接口、數據等層面。

2. 核心業務與普通業務分離。交易和行情服務是券商交易APP的核心業務,應該和其他業務進行有效隔離,避免其他應用對核心業務產生任何影響。例如一個運營活動的高峰流量,就不應該對核心交易行情服務產生任何影響。

3. 按照業務和主數據進行合理拆分。可以按照各類業務模塊來進行服務拆分,例如資訊、理財、增值服務、業務辦理、賬戶賬單等,每個業務模塊自然拆分成一個微服務組件。另外,按照主數據屬性來分類也是一個不錯的維度,例如在CRM中的各類數據維度,可以按照主數據屬性來進行劃分,例如資產類、賬戶類、交易流水類等。

2.2 業務梳理流程

按照上述拆分原則進行劃分,原有的單體應用已經拆分成多個微服務業務集群。如下圖1所示。除了按照業務劃分的業務組件之外,還有一些通用的功能性組件和非功能性組件,例如統一權限校驗、日誌統一處理服務、報警服務等,將在後續詳細說明。

基於 SpringCloud 的微服務治理架構落地實踐

三 技術架構實現

SpringCloud的總體組件架構圖如下圖2所示。

基於 SpringCloud 的微服務治理架構落地實踐

開箱即用的特性使得SpringCloud比較容易上手,需要哪個功能,就通過maven引入響應子系統組件,符合各個層次的使用者,也符合各類不同應用場景特點。微服務改造是一個漸變的過程,不必一開始就使用所有功能。按照目前自身的技術條件與保障能力,同時參照應用自身特點,我們使用瞭如下幾類組件,下面分別進行詳細說明。

3.1 服務註冊與發現:Eureka集群

目前SpringCloud支持的服務註冊有Consul和Eureka。Consul需要獨立部署,脫離Spring框架;而Eureka天然集成在SpringCloud中,本身就是一個SpringBoot項目。這2者特點可以通過如下表1展示。

基於 SpringCloud 的微服務治理架構落地實踐

可以看出,Consul在整體功能上比Eureka更加豐富和完備,但同時也增加了一定的複雜性;Eureka相對來說更加輕量,天然溶於SpringCloud體系,雖然缺失一部分功能,但是對於一般性的微服務集群來說已經足夠。因此,我們採用了Eureka來作為服務發現與註冊組件。下圖為目前已經接入Eureka組件的所有拆分的微服務實例。目前來看,數量還不算太多,後期隨著業務的發展,會不斷增加微服務實例。如下圖3所示:

基於 SpringCloud 的微服務治理架構落地實踐

實踐經驗

從業務量上來說,一個數據中心站點上,一般掛載2個實例做負載均衡即可滿足一般業務需求,但在一些請求量較大的服務上,我們會增加微服務的實例數量。例如對於我們的一個行情增值微服務組件,由於早高峰tps接近6000,就掛載了6個實例。因此通過實際業務量的請求數據監控,我們可以動態調配微服務組件的掛載實例,從而滿足業務需求,保障服務的HA,同時提高了系統資源的使用效率。

對於Eureka服務本身來說,也是掛載了2個實例,通過客戶端的Failover協議同時配置2個實例,來實現服務註冊的高可用。需要在每個註冊到Eureka集群中微服務組件均增加此配置即可:eureka.client.serviceUrl.defaultZone=http://ip1:20001/eureka/,http://ip2:20001/eureka/。

如果超過2個Eureka實例,則通過繼續以“,”分隔連上。這點來說,和ActiveMQ的連接使用異曲同工。

從我們實際運行來看,目前Eureka組件應用邏輯簡單、穩定。當一臺Eureka Server實例down機後,所有client實際均不受影響。當然從Eureka自身來說,也存在弱一致性的問題,特別是對於微服務節點在重啟時候,可能會存在某服務在一個心跳週期不可用的情況,待心跳超時隔離以後,就恢復正常了。其實這也是為了最大程度保證可用性,可見確實只滿足了AP。官方目前Eureka2.X版本將不再開源,基於此考慮,後續我們也會持續關注替代方案Consul,或者其他服務註冊框架。

3.2 統一接入網關:Zuul

所謂“一夫當關,萬夫莫開”,統一接入網關組件作為服務的統一入口,地位足夠重要。Zuul網關主要實現了請求服務路由、負載均衡、統一校驗等功能。在服務路由和負載均衡方面,和nginx相當類似。Zuul通過自身註冊到Eureka,通過url匹配的serviceID配置,即可實現服務路由。

目前我們在Zuul網關中實現了校驗token功能,通過ZuulFilter,可以輕鬆做到所有外部請求的token統一校驗。目前還沒有把其他校驗類邏輯放到Zuul中,一些請求流量統計的功能,我們在nginx層面做了攔截實現。

3.2.1 Zuul1 or Zuul2,同步or異步?

Zuul2版本已經與今年5月份發佈,相比於Zuul1,其強大的事件驅動和異步非阻塞調用特性,將支持超高併發接入並轉發。其核心是引入了高性能netty框架,從請求接入到調用外部組件,全部採用netty事件驅動模式來實現。而Zuul1則採用傳統servlet同步請求的方式進行處理,每個連接分配一個線程,所有的線程均從線程池中獲取,一旦外部連接併發量很大的話,線程數將急劇上升,一旦處理線程阻塞,則最終將耗盡容器中的線程池內的線程,造成容器無法繼續接受新的請求,因此才有了Hystrix熔斷組件來解決服務耗盡資源的解決方案。

雖然Zuul1的同步阻塞帶來了吞吐量的瓶頸,但是整個處理模塊比較簡單,從請求接入到處理,再到響應,整個流程都在一個線程中處理,方便了開發和調試跟蹤。而Zuul2則將接入請求全部放入netty的事件隊列中,並分別由不同的線程進行接入、處理、響應,從維護和跟蹤上面來看,更加複雜和難維護。

其實對於一般的應用場景來說,併發量不會太大,其實用Zuul1就能夠滿足要求,而且開發維護更簡單;但是如果面對高併發的應用場景,例如QPS>1000時,則建議採用Zuul2,其異步非阻塞特性,將輕鬆接入高併發連接,並配合其EventLoop和pipeline機制,可以保證所有的連接都能被接收和處理,基本不存在連接被拒絕訪問的情況。

從Netflix給出的性能結論來看,Zuul2的性能比Zuul1大致提升了20%,可見從吞吐量上面來說,提升能力有限;但是比較明確的是,Zuul2的連接數管理方面要明顯好於Zuul1,可以支持超大規模的併發連接處理。從這點上面來說,其性能應該接近於Nginx。可參考如下Zuul演進圖4.

基於 SpringCloud 的微服務治理架構落地實踐

3.2.2 實踐經驗

目前實際項目中,生產系統還是以Zuul1來作為API網關使用。因為目前我們只是對於內部系統API、外部一些核心低頻API調用走Zuul,併發量不太大,還能扛住;對於外部應用的一些高併發API調用,例如行情相關的接口QPS>6000,還是從Nginx接入,直接轉發到SpringBoot微服務組件。

從未來趨勢來看,為了實現微服務的全覆蓋和完整性,後期將把nginx的接入請求全部轉到Zuul上來,到時將必須升級到Zuul2上來,沒的說。

3.3 服務RPC調用:Feign

Feign是一種http方式的聲明式調用,目前SpringCloud默認使用java原生的HttpURLConnection進行http調用,同時通過jar包引入還可以支持Apache的httpclient、OKhttp等。Feign底層依靠Ribbon實現了調用各個實例的負載均衡,從效率上面來說,不佔太大優勢,但是從簡單易用上面來看絕對是一個重要的亮點;而且和自動熔斷降級組件Hystrix天熱融合,優勢明顯。使用Feign非常簡單,只需要創建一個調用服務的接口方法,標記@FeignClient即可像本地方法一樣調用。

3.3.1 自動降級熔斷:Hystrix

在微服務集群中通常會有很多個服務之間的調用,例如通過Feign調用。各個服務之間的調用最終形成了網狀模型。如果某一些基礎服務不可用,而上游服務持續不斷調用此基礎服務,可能導致整個系統故障,也就是“雪崩效應”。下圖5展示了常見的場景。

基於 SpringCloud 的微服務治理架構落地實踐

3.3.2 斷路器機制

斷路器很好理解,當Hystrix Command請求後端服務失敗數量超過一定比例(默認50%),斷路器會切換到開路狀態(OPEN)。這時所有請求會直接失敗而不會發送到後端服務。斷路器保持在開路狀態一段時間後(默認5秒),自動切換到半開路狀態(HALF-OPEN)。這時會判斷下一次請求的返回情況,如果請求成功, 斷路器切回閉路狀態(CLOSED),否則重新切換到開路狀態(OPEN)。Hystrix的斷路器就像我們家庭電路中的保險絲,一旦後端服務不可用,斷路器會直接切斷請求鏈,避免發送大量無效請求影響系統吞吐量,並且斷路器有自我檢測並恢復的能力。

3.3.3 Fallback

Fallback相當於是降級操作。對於查詢操作,我們可以實現一個fallback方法,當請求後端服務出現異常的時候,可以使用fallback方法返回的值。 fallback方法的返回值一般是設置的默認值或者來自緩存。例如在一個熱門推薦股票榜單功能中,如果Feign調用失敗,則自動在Fallback中返回本地緩存中的默認榜單即可,成功實現了推薦榜單服務的降級。

3.3.4 資源隔離

在Hystrix中,主要通過線程池來實現資源隔離。通常在使用的時候我們會根據調用的遠程服務劃分出多個線程池。例如調用產品服務的Command放入A線程池,調用賬戶服務的Command放入B線程池。這樣做的主要優點是運行環境被隔離開了。就算調用服務的代碼存在bug或者由於其他原因導致自己所在線程池被耗盡時,不會對系統的其他服務造成影響。但是帶來的代價就是維護多個線程池會對系統帶來額外的性能開銷。如果是對性能有嚴格要求而且確信自己調用服務的客戶端代碼不會出問題的話,可以使用Hystrix的信號量(Semaphores)來隔離資源。

例如,在我們實際生產系統中,如果服務調用不太頻繁時,減少線程數量,節約系統資源,可以在配置文件中顯示設置隔離線程池的線程規模=2:hystrix.threadpool.default.coreSize=2。

3.3.5 實踐經驗

由於Feign組件集成了Ribbon和Hystrix,其服務降級和熔斷的參數配置較為複雜,如果不看完源碼,則只能靠系統測試來一一驗證了。例如服務調用超時這類場景,其默認的服務超時設置為1秒,這對一些外部較複雜的服務調用來說是不太合適的,因此需要在配置文件中顯式聲明hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000,將其擴大到5秒超時。另外對於服務的重試次數,也需要進行謹慎設置,一旦被調用方服務沒有做好冪等性防護,可能會帶來意想不到的破壞。

3.4 接口調用鏈分析監控:Zipkin

Zipkin 是一個開放源代碼分佈式的跟蹤系統,由Twitter公司開源,它致力於收集服務的定時數據,以解決微服務架構中的延遲問題,包括數據的收集、存儲、查找和展現。每個服務向zipkin報告計時數據,zipkin會根據調用關係通過Zipkin UI生成依賴關係圖,顯示了多少跟蹤請求通過每個服務,該系統讓開發者可通過一個 Web 前端輕鬆的收集和分析數據,例如用戶每次請求服務的處理時間等,可方便的監測系統中存在的瓶頸。

Zipkin提供了可插拔數據存儲方式:In-Memory、MySql、Cassandra以及Elasticsearch。下圖6為我們實際場景中的一個接口的調用鏈分析。

基於 SpringCloud 的微服務治理架構落地實踐

基於 SpringCloud 的微服務治理架構落地實踐

上方的圖形展示了3個微服務之間的調用關係;下方的甘特圖則展示了調用鏈的各個服務耗時分佈。對於分析調用鏈層次多的複雜接口具有很好的分析作用。

實踐經驗

在我們實際系統中,對於接口的採樣率有個配置:spring.sleuth.sampler.percentage=0.2,表示只採樣20%的接口入庫存儲可展示,對於接口請求量巨大的應用來說,避免接口量太大消耗太多內存。

3.5 應用服務監控SpringBootAdmin

SpringBootAdmin提供了開箱即用的監控工具箱,可以看各個註冊在Eureka下個微服務實例的運行全貌,如下圖7所示。首先在面板上面展示了所有能監控到的應用實例,具體到IP和端口。Status包括UP和DOWN,分別表示正常啟動和未啟動。

基於 SpringCloud 的微服務治理架構落地實踐

點擊detail可以進入應用的詳細監控頁面。在詳情頁面概覽中包含了應用的全貌信息,包括JVM佔用的內存情況、類加載、GC、DataSource數據庫連接佔用情況快照,如下圖8所示。還提供有實時刷新功能,每隔一秒動態展示系統的快照,比JavaVisualVM和jConsole更加方便和直觀。我們用的較多的是DataSource連接數佔用情況分析,如果Active連接數長期佔用率較高,就必須考慮應用對數據庫的優化了。

基於 SpringCloud 的微服務治理架構落地實踐

基於 SpringCloud 的微服務治理架構落地實踐

在監控詳情頁面中,還可以看到Metrics、Environment、Logging、JMX、Threads、Trace、Heapdump幾個欄目。

3.5.1 Metrics

Metrics主要展示了接口調用次數和耗時統計(Counter和Gauges),便於查看微服務內部接口的運行情況。如下圖9所示。

基於 SpringCloud 的微服務治理架構落地實踐

3.5.2 Environment

主要提供該服務的所有配置項。包括系統層級和應用的配置列表。

3.5.3 Logging

提供動態改變應用log打印級別的功能。在系統出現異常而沒有查到有效日誌的情況下,可以動態調整logging的日誌級別,例如調整為debug級別,可以讓應用立即以debug級別進行日誌打印,方便查看系統詳細異常情況。其實底層還是使用了JMX實現,需要在logback配置中增加jmx支持配置,如下圖10所示。

基於 SpringCloud 的微服務治理架構落地實踐

3.5.4 JMX

JMX欄目將顯示應用所有JMX對象,如果有一些自定義的JMX MBean,可以通過此處操作動態執行操作,實現應用配置的動態熱變更,如下圖11所示。

基於 SpringCloud 的微服務治理架構落地實踐

3.5.5 Threads

主要提供當前線程堆棧的快照信息。通過查看線程全貌快照,可以看到當前所有線程的運行狀態,如果發現某類線程組全部都屬於running,可能需要擴容線程池。如果發現大部分線程高峰期一直都是waiting狀態,可能需要縮減線程池規模。如果發現大部分線程都在等待某一類資源對象鎖,則表示該類資源存在瓶頸,需要進行優化調整。合理的調整各類線程池規模,可以讓系統運行的更高效,提高系統資源利用率,如下圖12所示。

基於 SpringCloud 的微服務治理架構落地實踐

3.5.6 Trace

主要提供對外接口的實時運行快照。在此不做詳細說明。

3.5.7 Heapdump

主要提供了實時獲取內存堆棧快照的下載功能。如果發現應用佔用內存一直很高,GC釋放效率低,則可以通過查看內存堆棧快照,作進一步分析,哪些大對象沒有及時釋放。

四 敏捷開發運維保障

4.1 運維保障

微服務的正常運轉離不開配套的監控體系和敏捷DevOps,大量的微服務實例需要自動化的監控和運維的支持。如下圖13為最終微服務系統的部署圖。

基於 SpringCloud 的微服務治理架構落地實踐

對於部署的微服務來說,實現了中間件和數據庫的完全獨立,大部分微服務組件都擁有獨立的數據庫和緩存資源。當然有些緩存可能存在公用的情況,這個具體場景具體分析。目前部署的環境還是VM,後續為了提高部署速度和資源動態升縮能力,會嘗試用docker來進行替代。

目前對於服務器資源的監控時zabbix,對於微服務應用的監控是SpringBootAdmin,另外對於接口API的監控我們還自研了一套接口可用性監控系統,基本上覆蓋了監控的大部分需求。從架構演進角度來看,後續監控會引入Prometheus+Grafana結合使用。

4.2 持續集成CI & 持續部署CD

微服務的快速部署和迭代,需要持續集成CI與持續部署CD的支持。目前在測試環境已經實現了CI與CD的全流程自動化運行,由於公司網絡的監管要求,目前生產系統網段無法直接連通代碼倉庫,因此目前CI與CD其實是分離的,當前仍然需要人工去做部署包的上傳,如下圖14所示。

基於 SpringCloud 的微服務治理架構落地實踐

目前已經在jekins上實現了大部分的微服務實例的持續部署,大大提升了部署的效率和準確性。而且通過Jenkins上面的部署歷史,也實現了投產的審計和可追溯,提升投產管理水平,如下圖15所示。

基於 SpringCloud 的微服務治理架構落地實踐

五 總結與展望

從目前微服務的搭建和治理來看,整體還處於一個入門級水準,但考慮到我們實際的技術水平和保障能力,也無法一步做到很完善的規模。微服務治理是不斷演進的一個過程,正如任何系統架構一樣,永遠沒有最完美的,只有最合適的。

目前整套系統只是使用了SpringCloud中的很小一部分組件,後續可能會增加統一配置中心ConfigServer,但其前提是生產系統網絡需要開通到Git庫訪問,仍需要和機房部門溝通。考慮到目前我們的服務器還是實體機+VM組合,後續仍然需要引入docker容器技術,為以後遷移到雲平臺做好準備。

本文主要從實際開發運維的角度闡述了基於SpringCloud的微服務體系,希望可以為各位同行快速搭建微服務系統提供一些參考和幫助。

歡迎工作一到五年的Java工程師朋友們加入Java程序員開發: 826775302

群內提供免費的Java架構學習資料(裡面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!


分享到:


相關文章: