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

一 概述

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

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


二 業務梳理

2.1 業務梳理原則

和大部分企業實施微服務流程同樣,首先就是肯定微服務重構的原則和目標。因爲證券行業的自身特色,肯定了以下幾個原則:nginx

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

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

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

2.2 業務梳理流程

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


圖一 微服務業務梳理組件分佈圖數據庫

三 技術架構實現

SpringCloud的整體組件架構圖以下圖2所示。

圖2 SpringCloud微服務技術架構組件圖後端

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

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

目前SpringCloud支持的服務註冊有Consul和Eureka。Consul須要獨立部署,脫離Spring框架;而Eureka自然集成在SpringCloud中,自己就是一個SpringBoot項目。這2者特色能夠經過以下表1展現。

表1 Consul和Eureka對比表

能夠看出,Consul在總體功能上比Eureka更加豐富和完備,但同時也增長了必定的複雜性;Eureka相對來講更加輕量,自然溶於SpringCloud體系,雖然缺失一部分功能,可是對於通常性的微服務集羣來講已經足夠。所以,咱們採用了Eureka來做爲服務發現與註冊組件。下圖爲目前已經接入Eureka組件的全部拆分的微服務實例。目前來看,數量還不算太多,後期隨着業務的發展,會不斷增長微服務實例。以下圖3所示:


圖3 Eureka管理視圖

實踐經驗

從業務量上來講,一個數據中心站點上,通常掛載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.


圖4 從Zuul1到Zuul2演進圖

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展現了常見的場景。

圖5 Hystrix服務降級示例圖

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爲咱們實際場景中的一個接口的調用鏈分析。

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

實踐經驗

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

3.5 應用服務監控SpringBootAdmin

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


圖7 SpringBootAdmin Dashboard

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



圖8 SpringBootAdmin 應用內全貌圖

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

3.5.1 Metrics

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


圖9 SpringBootAdmin 應用內Metrics圖

3.5.2 Environment

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

3.5.3 Logging

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


圖10 SpringBootAdmin 應用內Logging圖

3.5.4 JMX

JMX欄目將顯示應用全部JMX對象,若是有一些自定義的JMX MBean,能夠經過此處操做動態執行操做,實現應用配置的動態熱變動,以下圖11所示。


圖11 SpringBootAdmin 應用內JMX圖

3.5.5 Threads

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


圖12 SpringBootAdmin 應用內Thread快照圖

3.5.6 Trace

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

3.5.7 Heapdump

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


四 敏捷開發運維保障

4.1 運維保障

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


圖13 微服務部署架構圖

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

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

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

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


圖14 CI&CD現狀圖

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


圖15 Jenkins自動部署圖

五 總結與展望

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

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

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

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

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

相關文章
相關標籤/搜索