微服務全流程分析

轉眼已經2020,距離微服務這個詞落地已通過去好多年!(我記得2017年就聽過這個詞)。然而今天我想一想什麼是微服務,其實並無一個很好的定義。爲何這樣說,按照微服務的定義:前端

微服務架構就是將一個龐大的業務系統按照業務模塊拆分紅若干個獨立的子系統,每一個子系統都是一個獨立的應用,它是一種將應用構建成一系列按業務領域劃分模塊的,小的自治服務的軟件架構方式,倡導將複雜的單體應用拆分紅若干個功能單1、鬆偶合的服務,這樣能夠下降開發難度、加強擴展性、便於敏捷開發,及持續集成與交付活動。git

根據這個定義,不難看出其實就是對複雜的業務系通通一作邏輯拆分,保持邏輯上的獨立。那麼邏輯上獨立就是一個服務這樣作真的是好嗎,如何界定:小、獨,還有要作一個事情,完成單一的業務,單一的功能要拆分出來,爲了獨立而獨立會不會致使拆的過細?不一樣人有不一樣的看法,咱們今天一塊兒探討微服務的過去和將來。web

微服務緣起

在沒有微服務以前,咱們最先的架構模式就是 MVC 模式,把業務邏輯分爲:表示層,業務邏輯層,數據訪問層。MVC模式隨着大前端的發展,從一開始的先後端不分離,到如今的先後端分離逐漸演進。這種演進好的一點是剝離了不一樣開發語言的開發環境和部署環境,使得開發較爲便利,部署更直接。然而問題是:這種模式仍然是單體應用模式,若是有一個改動須要上線,你不得不由於這個改動去考慮更多,由於你沒法估量在這麼大致量的代碼中你的一個改動會不會引起蝴蝶效應。spring

另外很重要的一點就是移動互聯網時代的到來,引起了用戶數幾何倍數暴增,傳統的單體應用模式已經沒法支撐用戶量暴漲的流量衝擊,互聯網人不得不作出加機器的無賴之舉,然而發現有的時候加機器都沒法搞定問題,由於邏輯調用過於耦合致使調用鏈複雜。繼而出現精簡調用流程,梳理調用路徑的舉措,因而演變出微服務這個概念。sql

其實在沒有微服務這個詞出現以前, 咱們也是這樣乾的,只是乾的不完全而已。好比說有一個信貸系統,信貸系統分爲貸前,貸中,貸後三步:數據庫

在微服務未出現以前,咱們大可能是單體應用,基本上一個工程包含全部,無所不能,因此很臃腫上。述這些模塊應該都是在一個工程中,可是按照業務作了代碼上的拆分。另外就是 RPC 框架併爲橫空出世,若是有服務上的拆分,好比不一樣部門之間調用對方提供的服務,那麼八九不離十確定定義的是HTTP 接口,由於通用。可是某些時候你們又怕 HTTP 接口性能差,關鍵服務不敢用。編程

微服務出現以後,你們以爲按照模塊分別部署好像是這麼回事,同時默默在內心嘀咕,之前我只用發佈一個工程,如今倒好,可能有個改動涉及3個服務,我一個小小的改動就要發佈3次,是否是增長了工做量,另外我之前都不用調接口的,如今依賴了一堆別的系統,系統調用這麼複雜,萬一別的系統有問題,我豈不是就被耽擱了!後端

在這種質疑中你們雖有抱怨可是也沒有放棄趕時髦,微服務開展的如火如荼,用戶中心獨立部署,風控系統單獨成型,支付中心全公司統一獨立,財務系統再也不各個業務各自爲戰而是統籌公司各個業務線統一規劃。按照業務抽象獨立以後,你們發現好像是這麼回事,用起來真香。雖然每次須要別的模塊的時候就須要找對應模塊進行接入,可是業務邏輯上清晰了呀,若是出了問題,不是本身的,那就是別人的,甩鍋很方便的(笑)。api

如何作微服務

由於微服務是功能粒度上的拆分,必然致使拆分以後的模塊變多。針對模塊與模塊之間的通訊與維護,又演變出以下問題:瀏覽器

  • 模塊與模塊之間如何通訊;
  • 每一個被拆分的微服務如何作負載均衡;
  • 服務如何作註冊,如何作發現;
  • 服務之間調用如何作限流,服務調用失敗如何作降級,流量異常如何作熔斷;
  • 服務調用是否能夠作統一的訪問控制;

針對這些問題,業界也在發展中慢慢演進出幾套通用的框架。理想中微服務框架應該具有這樣的能力:

基於上述微服務框架應該具有的能力,咱們來分析目前能夠落地的微服務框架的具體實現。

目前國內用的最多的無外乎是兩套框架:Dubbo,Spring Cloud。Dubbo你們都很熟悉,從開源到無人維護再到從新衝擊Apache頂級項目。可是Dubbo更加準確來講是一個分佈式服務框架,致力於提供高效的RPC遠程服務調用方案以及SOA服務治理方案。說白了就是個分佈式遠程服務調用框架

Dubbo

從Dubbo官網給的圖來看Dubbo的總體架構:

模塊註解:

  • Provider: 暴露服務的服務提供方
  • Consumer: 調用遠程服務的服務消費方
  • Registry: 服務註冊與發現的註冊中心
  • Monitor: 統計服務的調用次調和調用時間的監控中心
  • Container: 服務運行容器

從上圖中不難看出Dubbo功能仍是很明確的:服務註冊於發現,服務監控。另外Dubbo也提供服務治理功能:

Dubbo提供了集羣容錯的能力,在管理後臺能夠快速的摘除失敗的服務。

對於咱們上面提到的一整套微服務應該提供的功能看,Dubbo只是提供了服務註冊與服務發現的功能。不能否認在這一項功能中,Dubbo作的是很是優秀的。

Spring Cloud

Spring Cloud 基於 Spring Boot,爲微服務體系開發中的架構問題,提供了一整套的解決方案——服務註冊與發現,服務消費,服務保護與熔斷,網關,分佈式調用追蹤,分佈式配置管理等。

服務註冊與發現

目前Spring Cloud 支持的服務註冊組件有 Consul,Eureka。Consul 不是 Spring 官方的項目,須要單獨部署,Eureka 被 Spring 官方收錄,自己屬於 Spring Cloud 體系中。

下面列出能夠被用做註冊中心的組件他們的特性對比:

特性 Euerka Consul Zookeeper etcd
服務健康檢查 可配支持 服務狀態,內存,硬盤等 (弱)長鏈接,keepalive 鏈接心跳
多數據中心 支持
kv 存儲服務 支持 支持 支持
一致性 raft paxos raft
cap ap cp cp cp
使用接口(多語言能力) http(sidecar) 支持 http 和 dns 客戶端 http/grpc
watch 支持 支持 long polling/大部分增量 全量/支持long polling 支持 支持 long polling
自身監控 metrics metrics metrics
安全 acl /https acl https 支持(弱)
spring cloud 集成 已支持 已支持 已支持 已支持

Consul

Consul 官網中介紹了 Consul 的如下幾個核心功能:

  • 服務發現(Service Discovery):提供 HTTP 與DNS 兩種方式。
  • 健康檢查(Health Checking):提供多種健康檢查方式,好比 HTTP 狀態碼、內存使用狀況、硬盤等等。
  • 鍵值存儲(KV Store):能夠做爲服務配置中心使用,相似 Spring Cloud Config。
  • 加密服務通訊(Secure Service Communication)
  • 多數據中心(Multi Datacenter):Consul 經過 WAN 的 Gossip 協議,完成跨數據中心的同步。

Consul 須要單獨部署,而不是與Spring集成的組件。

Eureka

Eureka 是 Spring Cloud NetFlix 默認的服務發現框架,但目前 2.0 版本已閉源,只剩下 1.9 版本的處於維護狀態。Eureka 使用盡力而爲同步的方式提供提供弱一致的服務列表。當一個服務註冊時,Eureka 會嘗試將其同步到其餘節點上,但不提供一致性的保證。 所以,Eureka 能夠提供過期的或是已不存在的服務列表(在服務發現場景下,返回舊的總比什麼也不返回好)。

若是在 15分鐘內超過85%的客戶端節點都沒有正常的心跳,那麼 Eureka 就會認爲客戶端與註冊中心出現了網絡故障(出現網絡分區),進入自我保護機制。

此時:

  • Eureka Server 會保護服務註冊表中的信息,再也不刪除服務。這是因爲若是出現網絡分區致使其餘微服務和該 Eureka Server 沒法通訊,Eureka Server 就會斷定這些微服務失效,但極可能這些微服務都是健康的。
  • Eureka Server 仍能接受新服務的註冊和查詢請求,但這些數據不會被同步到其餘節點。
  • 當網絡恢復時,這個 Eureka Server 節點的數據會被同步到其餘節點中。

優勢:

Eureka Server 能夠很好的應對因網絡故障致使部分節點失聯的狀況,而不會像 ZK 那樣若是有一半不可用的狀況會致使整個集羣不可用。

服務網關

微服務的拆分致使服務分散,若是一個大的業務要對外提供輸出,每一個服務單獨對外提供調用對接入方不友好而且調用也會很複雜。因此出現了網關,網關主要實現請求的路由轉發,負載均衡,統一校驗,請求過濾等功能。

目前社區主流的網關有三個:Zuul,Kong,Spring Cloud GateWay。

Zuul

Zuul 是 Netflix 公司的開源項目,Spring Cloud 在 Netflix 項目中也已經集成了 Zuul,依賴名叫:spring-cloud-starter-netflix-zuul。Zuul構建於 Servlet 2.5,兼容 3.x,使用的是阻塞式的 API,不支持長鏈接,好比 websockets。咱們如今說的 Zuul 指 Zuul 1.x,Netflix 最新的 Zuul 2.x一直跳票,因此 Spring Cloud 在Zuul 2.x沒有出的時候依靠社區的力量發展出了新的網關組件:Spring Cloud Gateway。

Zuul 的核心功能就是基於 Servlet 提供了一系列的過濾器:

  • 身份認證與安全:識別每一個資源的驗證要求,並拒絕那些與要求不符的請求。
  • 審查與監控:在邊緣位置追蹤有意義的數據和統計結果,從而帶來精確的生產視圖。
  • 動態路由:動態地將請求路由到不一樣的後端集羣。
  • 壓力測試:逐漸增長指向集羣的流量,以瞭解性能。
  • 負載分配:爲每一種負載類型分配對應容量,並啓用超出限定值的請求。
  • 靜態響應處理:在邊緣位置直接創建部分相應,從而避免其轉發到內部集羣。

Spring Cloud Gateway

Spring Cloud Gateway 構建於 Spring 5+,基於 Spring Boot 2.x 響應式的、非阻塞式的 API。同時,它支持 Websockets,和 Spring 框架緊密集成,開發體驗相對來講十分不錯。SpringCloud Gateway 是基於WebFlux框架實現的,而WebFlux框架底層則使用了高性能的Reactor模式通訊框架Netty。

整體來講 Spring Cloud Gateway 與Zuul 功能差異不大,最大的出入是在底層性能的提高上。

Zuul 自己是基於 Servlet 容器來實現的過濾,Servlet採用的是單實例多線程的處理方案,Servlet會爲每個Request分配一個線程,若是當前線程比較耗時那麼會一直等到線程處理完畢纔會返回。因此說 Zuul 是基於servlet之上的一個阻塞式處理模型。

同步阻塞模型對於網關這種比較在乎響應耗時和調用頻繁的組件來講,必然會引起一些性能問題,因此Zuul 2已經作出了改良,從Zuul 2開始已經使用Netty。可是不幸的是Spring 官方已經對它的更新頻率感到失望因此縱然更新了也沒有被選用。

Spring Cloud Gateway 底層基於Webflux。Webflux模式替換了舊的Servlet線程模型。用少許的線程處理request和response io操做,這些線程稱爲Loop線程。Webflux的Loop線程,正好就是著名的Reactor 模式IO處理模型的Reactor線程,若是使用的是高性能的通訊框架Netty,這就是Netty的EventLoop線程。

因此總體來看,Spring Cloud Gateway 的性能要比目前在用的 Zuul 高。可是Webflux的編程方式可能你們不是很能接收。

服務降級

降級限流在微服務中屬於銀彈,通常不用,一旦用上那就是拯救宇宙般存在。

目前業界通用的降級限流工具主要有3款:Hystrix,Sentinel,Resilience4j。

Hystrix 的關注點在於以 隔離熔斷 爲主的容錯機制,超時或被熔斷的調用將會快速失敗,並能夠提供 fallback 機制。Hystrix 是元老級別的存在,可是在18年11月 Netflix 官方宣佈中止更新(就是這麼不靠譜,說跳票就跳票)。雖然中止更新,可是社區又推出了新的替代工具:Resilience4j。

Resilience4j 的模塊化作的比較好,將每一個功能點(如熔斷、限速器、自動重試)都拆成了單獨的模塊,這樣總體結構很清晰,用戶也只須要引入相應功能的依賴便可;另外resilience4j 是針對 Java 8 和函數式編程設計的,API 比較簡潔優雅。同時與 Hystrix 相比,Resilience4j 增長了簡單的限速器和自動重試特性,使用場景更加豐富。

相比 Hystrix , Resilience4j的優點在於:

  • 針對 Java 8 和函數式編程設計,提供函數式和響應式風格的 API;
  • 增長了 rate limiting 和 automatic retrying 兩個模塊。其中 rate limiting 引入了簡單的速率控制實現,補充了流量控制這一塊的功能;
  • 而 automatic retrying 則是封裝了自動重試的邏輯,簡化了異常恢復的流程。

Resilience4j 屬於一個新興項目,社區也在蓬勃發展。總的來講,Resilience4j 是比較輕量的庫,在較小較新的項目中使用仍是比較方便的,可是 Resilience4j 只包含限流降級的基本場景,對於很是複雜的企業級服務架構可能沒法很好地 cover 住;同時 Resilience4j 缺少生產級別的配套設施(如提供規則管理和實時監控能力的控制檯)。

Sentinel 一款面向分佈式服務架構的輕量級流量控制組件,主要以流量爲切入點,從流量控制、熔斷降級、系統自適應保護等多個維度來幫助用戶保障服務的穩定性。

Sentinel 的核心思想:根據對應資源配置的規則來爲資源執行相應的流控/降級/系統保護策略。在 Sentinel 中資源定義和規則配置是分離的。用戶先經過 Sentinel API 給對應的業務邏輯定義資源,而後能夠在須要的時候動態配置規則。

總體功能對比:

Sentinel Hystrix Resilience4j
隔離策略 信號量隔離(併發線程數限流) 線程池隔離/信號量隔離 信號量隔離
熔斷降級策略 基於響應時間、異常比率、異常數 基於異常比率 基於異常比率、響應時間
實時統計實現 滑動窗口(LeapArray) 滑動窗口(基於 RxJava) Ring Bit Buffer
動態規則配置 支持多種數據源 支持多種數據源 有限支持
擴展性 多個擴展點 插件的形式 接口的形式
基於註解的支持 支持 支持 支持
限流 基於 QPS,支持基於調用關係的限流 有限的支持 Rate Limiter
流量整形 支持預熱模式、勻速器模式、預熱排隊模式 不支持 簡單的 Rate Limiter 模式
系統自適應保護 支持 不支持 不支持
控制檯 提供開箱即用的控制檯,可配置規則、查看秒級監控、機器發現等 簡單的監控查看 不提供控制檯,可對接其它監控系統

從上面的參照看,Sentinel 的功能相對要多一些,可是多並不意味着全部,合適的纔是最好的,對於你用不到的功能,簡單纔是美麗。

統一配置中心

統一配置中心概念的提出也是伴隨着微服務架構出現纔出現,單體應用的時候全部的配置均可以集成在服務之中,多應用的時候若是每一個應用都持有一份配置可能會有相同配置冗餘的狀況;若是一共有2000臺機器,若是一個配置發生更改,是否要登陸每一臺機器從新更改配置呢;另外,更多的配置必然會帶來管理上的混亂,若是沒有集中管理的地方必然會愈來愈亂。

分佈式配置管理的本質基本上就是一種推送-訂閱模式的運用。配置的應用方是訂閱者,配置管理服務則是推送方。其中,客戶端包括管理人員publish數據到配置管理服務,能夠理解爲添加/更新數據;配置管理服務notify數據到訂閱者,能夠理解爲推送。配置管理服務每每會封裝一個客戶端庫,應用方則是基於該庫與配置管理服務進行交互。在實際實現時,客戶端庫多是主動拉取(pull)數據,但對於應用方而言,通常是一種事件通知方式。

選型一個合格的配置中心,至少須要知足以下4個核心需求:

  • 非開發環境下應用配置的保密性,避免將關鍵配置寫入源代碼;
  • 不一樣部署環境下應用配置的隔離性,好比非生產環境的配置不能用於生產環境;
  • 同一部署環境下的服務器應用配置的一致性,即全部服務器使用同一份配置;
  • 分佈式環境下應用配置的可管理性,即提供遠程管理配置的能力。

Diamond

最開始我接觸過的配置中心是淘寶的 Diamond,Diamond中的數據是簡單的key-value結構。應用方訂閱數據則是基於key來訂閱,未訂閱的數據固然不會被推送。

Diamond是無單點架構,在作更新配置的時候只作三件事:

  • 寫數據庫
  • 寫本地
  • 通知其餘機器到數據庫拉更新

本地的設計就是爲了緩存,減小對數據庫的壓力。做爲一個配置中心,高可用是最主要的需求。如何保持高可用,Diamond持有多層的數據存儲,數據被存儲在:數據庫,服務端磁盤,客戶端緩存目錄,以及能夠手工干預的容災目錄。 客戶端經過API獲取配置數據按照固定的順序去不一樣的數據源獲取數據:容災目錄,服務端磁盤,客戶端緩存。

Diamond除了在容災上作了不少方案,在數據讀取方面也有不少特色。客戶端採用推拉結合的策略在長鏈接和短鏈接之間取得一個平衡,讓服務端不用太關注鏈接的管理,又能夠得到長鏈接的及時性。

使用Diamond的流程:

發佈配置:

讀取配置:

Diamond server是無中心節點的邏輯集羣,這樣就能避免單點故障。Diamond的同質節點之間會相互通訊以保證數據的一致性,每一個節點都有其它節點的地址信息,其中一個節點發生數據變動後會響應的通知其餘節點,保證數據的一致性。

爲了保證高可用,client還會在app端緩存一個本地文件,這樣即便server不可用也能保證app可用。Client不斷長輪詢server,獲取最新的配置推送,儘可能保證本地數據的時效性。

Client默認啓動週期任務對server進行長輪詢感知server的配置變化,server感知到配置變化就發送變動的數據編號,客戶端經過數據編號再去拉取最新配置數據;不然超時結束請求(默認10秒)。拉取到新配置後,client會通知監聽者(MessageListener)作相應處理,用戶能夠經過Diamond#addListener監聽。

可是Diamond通常用途是作KV存儲,若是用來作配置中心,他提供的能力不是太符合。

能夠看到早期的配置中心處理的東西仍是比較簡單,那個時候業務沒有那麼複雜,讀取配置和更新配置沒有那麼多花樣,持久化存儲和本地緩存,長鏈接更新就能夠。可是如今的配置中心隨着技術的發展承擔的做用可能更多,

Spring Cloud Config

Spring Cloud Config 做爲Spring官方提供的配置中心可能比較符合外國人的習慣:

Spring Cloud Config將不一樣環境的全部配置存放在git 倉庫中,服務啓動時經過接口拉取配置。遵循{ServiceID}-{profile}.properties的結構,按照profile拉取本身所需的配置。

當開發者修改了配置項以後,須要結合spring config bus將配置通知到對應的服務,實現配置的動態更新。

能夠看到,Spring Cloud Config已經具有了一個配置中心的雛形,能夠知足小型項目對配置的管理,但仍然有着不少侷限性。配置使用git庫進行管理,那麼git庫的權限如何來判斷?不一樣環境的安全性也得不到保障。配置的添加和刪除,配置項的彙總,也只能經過git命令來實現,對運維人員也並不友好。

Apollo

Apollo(阿波羅)是攜程框架部門研發的開源配置管理中心,可以集中化管理應用不一樣環境、不一樣集羣的配置,配置修改後可以實時推送到應用端,而且具有規範的權限、流程治理等特性。

Apollo 支持4個維度管理 Key-Value 格式的配置:

  • application(應用):實際使用配置的應用,Apollo客戶端在運行時須要知道當前應用是誰,從而能夠去獲取對應的配置;每一個應用都須要有惟一的身份標識 – appId,應用身份是跟着代碼走的,因此須要在代碼中配置。
  • environment(環境):配置對應的環境,Apollo客戶端在運行時須要知道當前應用處於哪一個環境,從而能夠去獲取應用的配置。
  • cluster(集羣):一個應用下不一樣實例的分組,好比典型的能夠按照數據中心分,把上海機房的應用實例分爲一個集羣,把北京機房的應用實例分爲另外一個集羣。對不一樣的cluster,同一個配置能夠有不同的值,如ZooKeeper地址。
  • namespace(命名空間):一個應用下不一樣配置的分組,能夠簡單地把namespace類比爲文件,不一樣類型的配置存放在不一樣的文件中,如數據庫配置文件,RPC配置文件,應用自身的配置文件等;應用能夠直接讀取到公共組件的配置namespace,如DAL,RPC等;應用也能夠經過繼承公共組件的配置namespace來對公共組件的配置作調整,如DAL的初始數據庫鏈接數。

Apollo配置中心包括:Config Service、Admin Service 和 Portal。

  • Config Service:提供配置獲取接口、配置推送接口,服務於Apollo客戶端;
  • Admin Service:提供配置管理接口、配置修改發佈接口,服務於管理界面Portal;
  • Portal:配置管理界面,經過MetaServer獲取AdminService的服務列表,並使用客戶端軟負載SLB方式調用AdminService。

上圖簡要描述了Apollo的整體設計,咱們能夠從下往上看:

  • Config Service提供配置的讀取、推送等功能,服務對象是Apollo客戶端;
  • Admin Service提供配置的修改、發佈等功能,服務對象是Apollo Portal(管理界面);
  • Config Service和Admin Service都是多實例、無狀態部署,因此須要將本身註冊到Eureka中並保持心跳;
  • 在Eureka之上咱們架了一層Meta Server用於封裝Eureka的服務發現接口;
  • Client經過域名訪問Meta Server獲取Config Service服務列表(IP+Port),然後直接經過IP+Port訪問服務,同時在Client側會作load balance、錯誤重試;
  • Portal經過域名訪問Meta Server獲取Admin Service服務列表(IP+Port),然後直接經過IP+Port訪問服務,同時在Portal側會作load balance、錯誤重試;
  • 爲了簡化部署,咱們實際上會把Config Service、Eureka和Meta Server三個邏輯角色部署在同一個JVM進程中。

客戶端設計:

上圖簡要描述了Apollo客戶端的實現原理:

  1. 客戶端和服務端保持了一個長鏈接,從而能第一時間得到配置更新的推送;
  2. 客戶端還會定時從Apollo配置中心服務端拉取應用的最新配置
    • 這是一個fallback機制,爲了防止推送機制失效致使配置不更新
    • 客戶端定時拉取會上報本地版本,因此通常狀況下,對於定時拉取的操做,服務端都會返回304 - Not Modified
    • 定時頻率默認爲每5分鐘拉取一次,客戶端也能夠經過在運行時指定System Property: apollo.refreshInterval來覆蓋,單位爲分鐘。
  3. 客戶端從Apollo配置中心服務端獲取到應用的最新配置後,會保存在內存中;
  4. 客戶端會把從服務端獲取到的配置在本地文件系統緩存一份;
    • 在遇到服務不可用,或網絡不通的時候,依然能從本地恢復配置
  5. 應用程序從Apollo客戶端獲取最新的配置、訂閱配置更新通知。

配置更新:

前面提到了Apollo客戶端和服務端保持了一個長鏈接,從而能第一時間得到配置更新的推送。

長鏈接其實是經過Http Long Polling實現的,具體而言:

  • 客戶端發起一個Http請求到服務端
  • 服務端會保持住這個鏈接60秒
    • 若是在60秒內有客戶端關心的配置變化,被保持住的客戶端請求會當即返回,並告知客戶端有配置變化的namespace信息,客戶端會據此拉取對應namespace的最新配置
    • 若是在60秒內沒有客戶端關心的配置變化,那麼會返回Http狀態碼304給客戶端
  • 客戶端在收到服務端請求後會當即從新發起鏈接,回到第一步

考慮到會有數萬客戶端向服務端發起長連,在服務端使用了async servlet(Spring DeferredResult)來服務Http Long Polling請求。

調用鏈路分析

服務調用鏈路分析在微服務中是幕後相當重要的使者,試想幾百個服務摻雜在一塊兒,你想屢出誰先調用了誰,誰被誰調用,若是沒有一個可監控的路徑,光憑腦子跟蹤那的多累。基於這種需求,各路大神們集中腦汁展開遐想弄出一套分佈式鏈路追蹤神器來。

在介紹調用鏈監控工具以前,咱們首先須要知道在微服務架構系統中常常會遇到兩個問題:

  • 跨服務調用發生異常,要求快速定位當前此次調用出問題在哪一步;
  • 跨服務的調用發生性能瓶頸,要求迅速定位出系統瓶頸應該如何作。

打個比方說咱們有兩個服務:訂單中心,庫存中心。用戶下單,先去查詢庫存系統,那麼調用鏈路分析系統對於一個下單查詢服務應該記錄什麼呢?咱們造出以下一張調用鏈路請求記錄表,表字段以下:

表字段說明:

  • id:自增id
  • span_id:惟一id
  • pspan_id:父級span_id
  • service_name:服務名稱
  • api:api路徑
  • stage:階段/狀態
  • timestamp:插入數據時的時間戳
id span_id p_span_id service_name api stage time_stamp
1 uid1 null order-center /shop/0001 cs t1
2 uid2 uid1 shop-center /getCount/0001 sr t2
3 uid2 uid1 shop-center /getCount/0001 ss t3
4 uid3 null order-center /shop/0001 cr t4

上表中的stage中的狀態解釋爲:

  • CS(Client Sent 客戶端發送):客戶端發送一個請求,表示span的開始;
  • SR(Server Received 服務端接收):服務端接收請求並開始處理它。(SR - CS)等於網絡的延遲;
  • SS(Server Sent 服務端發送):服務端處理請求完成,開始返回結束給服務端。(SR - SS)表示服務端處理請求的時間;
  • CR(Client Received 客戶端接收):客戶端完成接受返回結果,此時span結束。(CR - CS)表示客戶端接收服務端數據的時間。

根據這個表咱們就能很快的分析上面提到的兩個問題:

若是以上任何一步有問題,那麼當前調用就不是完整的,咱們必然能追蹤出來;

經過每一步的調用時間進行分析,咱們也必然知道阻塞在哪一步,從而對調用慢的地方進行優化。

現有的分佈式Trace基本都是採用了google 的 Dapper 標準。

標準。

Dapper的思想很簡單,就是在每一次調用棧中,使用同一個TraceId將不一樣的server聯繫起來。

一次單獨的調用鏈也能夠稱爲一個span,dapper記錄的是span的名稱,以及每一個span的ID和父ID,以重建在一次追蹤過程當中不一樣span之間的關係。

對於一個特定的span,記錄從Start到End,首先經歷了客戶端發送數據,而後server接收數據,而後server執行內部邏輯,這中間可能去訪問另外一個應用。執行完了server將數據返回,而後客戶端接收到數據。

在整個過程當中,TraceId和ParentId的生成相當重要。首先解釋下TraceIdParentIdTraceId是標識這個調用鏈的Id,整個調用鏈,從瀏覽器開始放完,到A到B到C,一直到調用結束,全部應用在此次調用中擁有同一個TraceId,因此才能把此次調用鏈在一塊兒。

既然知道了此次調用鏈的整個Id,那麼每次查找問題的時候,只要知道某一個調用的TraceId,就能把全部這個Id的調用所有查找出來,可以清楚的知道本地調用鏈通過了哪些應用,產生了哪些調用。可是還缺一點,那就是鏈。

基於這種需求,目前各大廠商都作出了本身的分佈式追蹤系統,目前國內開源的有阿里的鷹眼,美團的CAT,京東的Hydra,還有廣爲人知的我的開源Apache頂級項目SkyWalking。國外的有Zipkin,Pinpoint。

Spring Cloud Sleuth + Zipkin

Spring Cloud Sleuth 實現了一種分佈式的服務鏈路跟蹤解決方案,經過使用Sleuth可讓咱們快速定位某個服務的問題。簡單來講,Sleuth至關於調用鏈監控工具的客戶端,集成在各個微服務上,負責產生調用鏈監控數據。

經過Sleuth產生的調用鏈監控信息,讓咱們能夠得知微服務之間的調用鏈路,可是監控信息只輸出到控制檯始終不太方便查看。因此咱們須要一個圖形化的工具,這時候就輪到Zipkin出場了。Zipkin是Twitter開源的分佈式跟蹤系統,主要用來收集系統的時序數據,從而追蹤系統的調用問題。

Spring Cloud Slueth 聚焦在鏈路追蹤和分析,將信息發送到Zipkin,利用 Zipkin的存儲來存儲信息,固然,Zipkin也可使用ELK來記錄日誌和展現,再經過收集服務器性能的腳本把數據存儲到ELK,則能夠展現服務器情況信息。

PinPoint

pinpoint數據分析很是完備的。提供代碼級別的可見性以便輕鬆定位失敗點和瓶頸,對於執行的sql語句,都進行了記錄。還能夠配置報警規則等,設置每一個應用對應的負責人,根據配置的規則報警,支持的中間件和框架也比較完備。Pinpoint 是一個完整的性能監控解決方案:有從探針、收集器、存儲到 Web 界面等全套體系。

Pinpoint 提供有 Java Agent 探針,經過字節碼注入的方式實現調用攔截和數據收集,能夠作到真正的代碼無侵入,只須要在啓動服務器的時候添加一些參數,就能夠完成探針的部署。

對於這一點,Zipkin 使用修改過的類庫和它本身的容器(Finagle)來提供分佈式事務跟蹤的功能。可是,它要求在須要時修改代碼。pinpoint是基於字節碼加強的方式,開發人員不須要修改代碼,而且能夠收集到更多精確的數據由於有字節碼中的更多信息。

相對來講,pinpoint界面顯示的更加豐富,具體到調用的DB名,zipkin的拓撲侷限於服務於服務之間。

SkyWalking

SkyWalking 和 pinpoint有一種既生瑜何生亮的感嘆。SkyWalking 邏輯上分爲四部分:

  1. 探針(SkyWalking-agent)
  2. 平臺後端(oap-server)
  3. 存儲(es)
  4. 用戶界面(apm-webapp)

探針基於不一樣的來源多是不同的(原生代理, SDK 以及 Zipkin, Jaeger 和 OpenCensus )、但做用都是收集數據、將數據格式化爲 SkyWalking 適用的格式。

平臺後端是一個支持集羣模式運行的後臺、用於數據聚合、數據分析以及驅動數據流從探針到用戶界面的流程、平臺後端還提供了各類可插拔的能力、如不一樣來源數據(如來自 Zipkin)格式化、不一樣存儲系統以及集羣管理、你甚至還可使用觀測分析語言來進行自定義聚合分析。

存儲是開放式的、你能夠選擇一個既有的存儲系統、如 ElasticSearch, H2 或 MySQL 集羣(Sharding-Sphere 管理)、也能夠選擇本身實現一個存儲系統。

用戶界面對於 SkyWalking 的最終用戶來講很是炫酷且強大、一樣它也是可定製以匹配你已存在的後端的。

總結

以上是微服務過程全鏈路過程當中須要經歷的階段,固然還不包括髮布系統的搭建,底層數據治理能力。因此提倡微服務能夠,可是真的作起來不是全部公司都能作獲得。小公司能作到服務拆分可是相應的配套設施不必定能跟上,大公司有人有錢有時間,才能提供這些基礎設施。微服務的路任重道遠,搭起來一套完整的設施並用於生產環境仍是挺有挑戰,新的一年但願我可以在踐行微服務的路上走下去,只有走的完整纔是微服務,走的不完整對於開發人員來講,那就是過分開發,就是災難。

相關文章
相關標籤/搜索