現代互聯網服務一般是使用複雜的、大規模的分佈式系統來實現的。這些應用程序每每是由大量的軟件模塊構建的,並且這些軟件模塊可能由不一樣的團隊開發,可能使用不一樣的編程語言,而且能夠跨多個物理設施跨越數千臺機器。在這種環境中,幫助理解系統行爲和性能問題推理的工具是很是寶貴的。git
微服務架構是一個分佈式架構,實際開發中,咱們按照業務要求劃分服務單元,一套系統每每會由多個業務單元構成。在這個場景中,一個請求可能須要經歷多個業務單元的處理才能完成響應,若是出現了錯誤或異常,很難定位。github
爲了解決這個問題,谷歌開源了分佈式鏈路追蹤組件Drapper,並發表論文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》介紹了Drapper的設計思想。在該論文的影響下,Twitter設計、研發並開源了分佈式鏈路追蹤系統Zipkin。golang
Zipkin是一個分佈式跟蹤系統,它能夠幫助收集時間數據,以此解決在微服務架構下的延遲問題。它同時提供了分佈式系統時間數據的收集和查詢功能。Zipkin的架構以下圖所示:docker
經過架構圖可知,Zipkin由Collector、Storage、API、UI共4個組件構成,Reporter由應用系統提供並收集數據,其工做原理大概以下:數據庫
Zipkin經過Trace結構表示對一次請求的跟蹤,一次請求由若干服務處理,每一個服務生成一個Span,同一請求的Span之間存在關聯關係,在UI組件中以樹形式展現。Span的主要數據模型以下所示:編程
字段 | 類型 | 說明 |
---|---|---|
traceId | string | 隨機生成,用於惟一標識一個追蹤信息,全部的span都包含此信息。 |
name | string | span的名稱,可以使用method命名,在UI組件中顯示 |
parentId | string | 父級span的編號,若爲空,則表示爲根span |
id | string | span的編號 |
timestamp | integer | span建立的時間 |
duration | integer | span持續時間 |
annotations | Annotation | 關聯一個事件,用時間戳解釋延遲信息 |
tags | Tags | span的標籤,用於搜索、顯示和分析 |
Zipkin官方已經推出各類常見語言的支持,如C#、go、Java、JavaScript、Ruby、Scala、PHP,另外社區也貢獻了Python、C/C++、Lua等語言的支持。bootstrap
本文將延續「go-kit微服務系列」,使用go-kit集成Zipkin實現算術運算服務的鏈路追蹤,這裏將包含兩個部分:segmentfault
gateway
中增長鏈路追蹤採集邏輯,同時在反向代理中增長追蹤設置。go-kit在tracing
包中默認添加了zipkin
的支持,因此集成工做將會比較輕鬆。在開始以前,須要下載如下依賴:api
# zipkin官方庫 go get github.com/openzipkin/zipkin-go # 下面三個包都是依賴,按需下載 git clone https://github.com/googleapis/googleapis.git [your GOPATH]/src/google.golang.org/genproto git clone https://github.com/grpc/grpc-go.git [your GOPATH]/src/google.golang.org/grpc git clone https://github.com/golang/text.git [your GOPATH]/src/golang.org/text 複製代碼
本次演練使用arithmetic_consul_demo
中的gateway
和register
兩個服務,複製該目錄並重命名爲arithmetic_trace_demo
,刪除discover
。瀏覽器
docker/docker-compose.yml
,在consul的基礎上增長zipkin配置信息(使用官方推薦的openzipkin/zipkin
),最終內容以下所示:version: '2' services: consul: image: progrium/consul:latest ports: - 8400:8400 - 8500:8500 - 8600:53/udp hostname: consulserver command: -server -bootstrap -ui-dir /ui zipkin: image: openzipkin/zipkin ports: - 9411:9411 複製代碼
docker
目錄,執行如下命令,啓動consul和zipkin。sudo docker-compose up
複製代碼
http://localhost:9411
檢查是否啓動成功。gateway
將做爲鏈路追蹤的第一站和最後一站,咱們須要截獲到達gateway
的全部請求,記錄追蹤信息。gateway
做爲外部請求的服務端,同時做爲算術運算服務的客戶端(反向代理內部實現)。
結合Zipkin的架構圖,須要在應用程序中集成Reporter組件,咱們使用官方提供的go包。代碼以下(默認設置了zipkin的url):
// 建立環境變量 var ( // consul環境變量省略 zipkinURL = flag.String("zipkin.url", "http://192.168.192.146:9411/api/v2/spans", "Zipkin server url") ) flag.Parse() var zipkinTracer *zipkin.Tracer { var ( err error hostPort = "localhost:9090" serviceName = "gateway-service" useNoopTracer = (*zipkinURL == "") reporter = zipkinhttp.NewReporter(*zipkinURL) ) defer reporter.Close() zEP, _ := zipkin.NewEndpoint(serviceName, hostPort) zipkinTracer, err = zipkin.NewTracer( reporter, zipkin.WithLocalEndpoint(zEP), zipkin.WithNoopTracer(useNoopTracer), ) if err != nil { logger.Log("err", err) os.Exit(1) } if !useNoopTracer { logger.Log("tracer", "Zipkin", "type", "Native", "URL", *zipkinURL) } } 複製代碼
咱們使用的傳輸方式爲http,可使用zipkin-go
提供的middleware/http
包,它採用裝飾者模式把咱們的http.Handler
進行封裝,而後啓動監聽便可,代碼以下所示:
//建立反向代理 proxy := NewReverseProxy(consulClient, zipkinTracer, logger) tags := map[string]string{ "component": "gateway_server", } handler := zipkinhttpsvr.NewServerMiddleware( zipkinTracer, zipkinhttpsvr.SpanName("gateway"), zipkinhttpsvr.TagResponseSize(true), zipkinhttpsvr.ServerTags(tags), )(proxy) 複製代碼
gateway
接收請求後,會建立一個span
,其中的traceId
將做爲本次請求的惟一編號,gateway
必須把這個traceId
「告訴」算術運算服務,算術運算服務才能爲該請求持續記錄追蹤信息。
在ReverseProxy
中可以完成這一任務的就是Transport
,咱們可使用zipkin-go
的middleware/http
包提供的NewTransport
替換系統默認的http.DefaultTransport
。代碼以下所示:
// NewReverseProxy 建立反向代理處理方法 func NewReverseProxy(client *api.Client, zikkinTracer *zipkin.Tracer, logger log.Logger) *httputil.ReverseProxy { //建立Director director := func(req *http.Request) { //省略 } // 爲反向代理增長追蹤邏輯,使用以下RoundTrip代替默認Transport roundTrip, _ := zipkinhttpsvr.NewTransport(zikkinTracer, zipkinhttpsvr.TransportTrace(true)) return &httputil.ReverseProxy{ Director: director, Transport: roundTrip, } } 複製代碼
這一步很關鍵!若不設置,會致使整個鏈路追蹤不完整。爲了解決這個問題,花費了很多時間,最後仍是經過zipkin-go
的README解開了疑惑。
完成以上過程,就能夠編譯運行了。
這一步與gateway
的處理方式同樣,再也不描述。
go-kit提供了對zipkin-go
的封裝,可直接調用中間件TraceEndpoint
對算術運算服務的兩個Endpoint
進行設置。代碼以下:
endpoint := MakeArithmeticEndpoint(svc) endpoint = NewTokenBucketLimitterWithBuildIn(ratebucket)(endpoint) //添加追蹤,設置span的名稱爲calculate-endpoint endpoint = kitzipkin.TraceEndpoint(zipkinTracer, "calculate-endpoint")(endpoint) //建立健康檢查的Endpoint healthEndpoint := MakeHealthCheckEndpoint(svc) healthEndpoint = NewTokenBucketLimitterWithBuildIn(ratebucket)(healthEndpoint) //添加追蹤,設置span的名稱爲health-endpoint healthEndpoint = kitzipkin.TraceEndpoint(zipkinTracer, "health-endpoint")(healthEndpoint) 複製代碼
transports.go
的MakeHttpHandler
方法。增長參數zipkinTracer
,而後在ServerOption中設置追蹤參數。代碼以下:// MakeHttpHandler make http handler use mux func MakeHttpHandler(ctx context.Context, endpoints ArithmeticEndpoints, zipkinTracer *gozipkin.Tracer, logger log.Logger) http.Handler { r := mux.NewRouter() zipkinServer := zipkin.HTTPServerTrace(zipkinTracer, zipkin.Name("http-transport")) options := []kithttp.ServerOption{ kithttp.ServerErrorLogger(logger), kithttp.ServerErrorEncoder(kithttp.DefaultErrorEncoder), zipkinServer, } //省略代碼 return r } 複製代碼
main.go
中調用MakeHttpHandler
。//建立http.Handler
r := MakeHttpHandler(ctx, endpts, zipkinTracer, logger)
複製代碼
至此,全部的代碼修改工做已經完成,下一步就是啓動測試了。
確保Consul
、Zipkin
、gateway
、register
四個服務已經正常運行,而後使用Postman進行請求測試(與以前相似,爲了方便查看數據,可多點幾回)。
在瀏覽器中打開http://localhost:9411
,點擊「Find Traces」按鈕,便可看到以下界面。詳細顯示了每一個請求執行的時間、span的數量、途徑的服務名稱等信息。
打開第一個請求,進入該請求的鏈路追蹤界面,以下圖所示。
經過該界面,咱們能夠知道請求鏈路中比較耗時的環節爲gateway-service
。緣由是:每次請求過來,程序都要到Consul中查詢服務實例,動態建立服務地址。
另外,點擊樹形結構的每一個span,能夠查看span的描述信息,這裏再也不展開描述。
本文使用go-kit的tracing
組件和zipkin-go
包,爲網關服務和算術運算服務增長了鏈路追蹤功能,以實例方式演示了在go-kit中集成Zipkin
的方式。示例比較簡單,但願對你有用!
本文首發於本人微信公衆號【兮一昂吧】,歡迎掃碼關注!