現代互聯網服務一般是使用複雜的、大規模的分佈式系統來實現的。這些應用程序每每是由大量的軟件模塊構建的,並且這些軟件模塊可能由不一樣的團隊開發,可能使用不一樣的編程語言,而且能夠跨多個物理設施跨越數千臺機器。在這種環境中,幫助理解系統行爲和性能問題推理的工具是很是寶貴的。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
的方式。示例比較簡單,但願對你有用!
本文首發於本人微信公衆號【兮一昂吧】,歡迎掃碼關注!