鏈路追蹤
現代網際網路服務通常是使用複雜的、大規模的分散式系統來實現的。這些應用程式往往是由大量的軟體模組構建的,而且這些軟體模組可能由不同的團隊開發,可能使用不同的程式語言,並且可以跨多個物理設施跨越數千臺機器。在這種環境中,幫助理解系統行為和效能問題推理的工具是非常寶貴的。
微服務架構是一個分散式架構,實際開發中,我們按照業務要求劃分服務單元,一套系統往往會由多個業務單元構成。在這個場景中,一個請求可能需要經歷多個業務單元的處理才能完成響應,如果出現了錯誤或異常,很難定位。
為了解決這個問題,谷歌開源了分散式鏈路追蹤元件Drapper,並發表論文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》介紹了Drapper的設計思想。在該論文的影響下,Twitter設計、研發並開源了分散式鏈路追蹤系統Zipkin。
Zipkin
Zipkin是一個分散式跟蹤系統,它可以幫助收集時間資料,以此解決在微服務架構下的延遲問題。它同時提供了分散式系統時間資料的收集和查詢功能。Zipkin的架構如下圖所示:
通過架構圖可知,Zipkin由Collector、Storage、API、UI共4個元件構成,Reporter由應用系統提供並收集資料,其工作原理大概如下:
- 在應用程式中嵌入追蹤器(Tracer),它使用Span記錄應用程式動作的時間和後設資料資訊;
- Reporter把Span傳送至Zipkin的資料收集器Collector;
- Collector通過Storage元件把資料儲存至資料庫;
- UI元件通過API介面查詢追蹤資料並顯示;
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等語言的支援。
實戰演練
Step-0:準備工作
本文將延續“go-kit微服務系列”,使用go-kit整合Zipkin實現算術運算服務的鏈路追蹤,這裡將包含兩個部分:
- 在閘道器
gateway
中增加鏈路追蹤採集邏輯,同時在反向代理中增加追蹤設定。 - 在算術運算服務的傳輸層和Endpoint層增加鏈路追蹤採集邏輯。
go-kit在tracing
包中預設新增了zipkin
的支援,所以整合工作將會比較輕鬆。在開始之前,需要下載以下依賴:
# 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
。
Step-1:Docker啟動Zipkin
- 開啟檔案
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
檢查是否啟動成功。
Step-2:修改gateway
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解開了疑惑。
完成以上過程,就可以編譯執行了。
Step-3:修改算術服務
建立追蹤器
這一步與gateway
的處理方式一樣,不再描述。
追蹤Endpoint
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)
複製程式碼
追蹤Transport
- 修改
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)
複製程式碼
至此,所有的程式碼修改工作已經完成,下一步就是啟動測試了。
Step-4:執行&測試
確保Consul
、Zipkin
、gateway
、register
四個服務已經正常執行,然後使用Postman進行請求測試(與之前類似,為了方便檢視資料,可多點幾次)。
在瀏覽器中開啟http://localhost:9411
,點選“Find Traces”按鈕,即可看到如下介面。詳細顯示了每個請求執行的時間、span的數量、途徑的服務名稱等資訊。
開啟第一個請求,進入該請求的鏈路追蹤介面,如下圖所示。
- 上半部分顯示:,該請求的執行時間為10.970毫秒、途徑的服務為2個、鏈路深度為3、span數量為3。
- 下半部分顯示:以樹形方式顯示span,直觀展示每個span的途徑服務、執行時間、span名稱等資訊。
通過該介面,我們可以知道請求鏈路中比較耗時的環節為gateway-service
。原因是:每次請求過來,程式都要到Consul中查詢服務例項,動態建立服務地址。
另外,點選樹形結構的每個span,可以檢視span的描述資訊,這裡不再展開描述。
總結
本文使用go-kit的tracing
元件和zipkin-go
包,為閘道器服務和算術運算服務增加了鏈路追蹤功能,以例項方式演示了在go-kit中整合Zipkin
的方式。示例比較簡單,希望對你有用!
本文參考
本文首發於本人微信公眾號【兮一昂吧】,歡迎掃碼關注!