背景
隨著互聯網技術的高速發展,以往單應用的服務架構已經很難處理如山洪般增長的信息數據,隨著云計算技術的大規模應用,以微服務、RESTful 為代表的各種軟件架構廣泛應用,跨團隊、跨編程語言的大規模分布式系統也越來越多。相對而言,現在要理解系統行為,追蹤診斷性能問題會復雜得多。
在單應用環境下,業務都在同一個服務器上,如果出現錯誤和異常只需要盯住一個點,就可以快速定位和處理問題;但是在微服務的架構下,功能模塊天然是分布式部署運行的,前后臺的業務流會經過很多個微服務的處理和傳遞,就連日志監控都會成為一個大問題(日志分散在多個服務器、無狀態服務下如何查看業務流的處理順序等),更不要說服務之間還有復雜的交互關系。
用戶的一個請求在系統中會經過多個子系統(或者多個微服務)的處理,而且是發生在不同機器甚至是不同集群,當發生異常時需要快速發現問題,并準確定位到是哪個環節出了問題。對系統行為進行跟蹤必須持續進行,因為異常的發生是無法預料的,有些甚至難以重現。跟蹤需要無所不在,否則可能會遺漏某些重要的故障點。
為了解決上述問題,分布式跟蹤系統 —— 一種幫助理解分布式系統行為、幫助分析性能問題的工具應運而生。
Distributed Tracing and Monitoring System
討論分布式跟蹤,就一定會談到 Dapper —— Google 公司研發并應用于自己生產環境的一款跟蹤系統(設計之初參考了一些 Magpie 和 X-Trace 的理念 )。Dapper 不僅為業內提供了非常有參考價值的實現,同步發表論文的也成為了當前分布式跟蹤系統的重要理論基礎。
Modern Internet services are often implemented as complex, large-scale distributed systems.These applications are constructed from collections of software modules that may be developed by different teams, perhaps in different programming languages, and could span many thousands of machines across multiple physical facilities. Tools that aid in understanding system behavior and reasoning about performance issues are invaluable in such an environment.
在這篇論文中,Google 提出了關于分布式跟蹤系統的一些重要概念:
Annotation-based,基于標注或植入點、埋點
在應用程序或中間件中明確定義全局標注(Annotation),一個特殊的ID,通過這個 ID 連接每一條請求記錄。當然,這需要代碼植入,在生產環境中可以通過一個通用組件開放給開發人員。跟蹤樹和span
在 Dapper 跟蹤樹(Trace tree)中,基本單元是樹節點(分配 spanid)。節點之間通過連線表示父子關系,通過 parentId 和 spanId 把所有的關系串聯起來,實現記錄業務流的作用。
Google Dapper 的理念影響了一批分布式跟蹤系統的發展,例如 2012 年,Twitter 公司嚴格按照 Dapper 論文的要求實現了 Zipkin (Scala 編寫,集成到 Twitter公司自己的分布式服務 Finagle );Uber 公司基于 Google Dapper 和 Twitter Zipkin 的靈感,開發了開源分布式跟蹤系統 Jaeger,例如 Jaeger 規范中同樣定義了 Span(跨度, 跨徑,兩個界限間的距離)。
然而,Google Dapper 的定位更準確的說是分析系統,并不能解決從生產服務中提取數據的難題,OpenCensus 項目為此提供了解決方案。
OpenCensus: A framework for distributed tracing
OpenCensus is a framework for stats collection and distributed tracing.
OpenCensus 項目是 Google 開源的一個用來收集和追蹤應用指標的第三方庫。OpenCensus 能夠提供了一套統一的測量工具:跨服務捕獲跟蹤跨度(span)、應用級別指標以及來自其他應用的元數據(例如日志)。OpenCensus 有如下一些主要特點:
- 標準通信協議和一致的 API :用于處理 metric 和 trace
- 多語言庫,包括Java,C++,Go,.Net,Python,PHP,Node.js,Erlang 和 Ruby
- 與 RPC 框架的集成,可以提供開箱即用的追蹤和指標。
- 集成的存儲和分析工具
- 完全開源,支持第三方集成和輸出的插件化
- 不需要額外的服務器或守護進程來支持 OpenCensus
- In process debugging:一個可選的代理程序,用于在目標主機上顯示請求和指標數據
OpenCensus Concepts
Tags | 標簽
OpenCensus 允許系統在記錄時將度量與維度相關聯。記錄的數據使我們能夠從各種不同的角度分析測量結果,即使在高度互連和復雜的系統中也能夠應付。
Stats | 統計
Stats 收集庫和應用程序記錄的測量結果,匯總、導出統計數據。
Trace | 跟蹤
Trace 是嵌套 Span (跨度)的集合。Trace 包括單個用戶請求的處理進度,直到用戶請求得到響應。Trace 通??缭椒植际较到y中的多個節點。跟蹤由 TraceId 唯一標識, Trace 中的所有 Span 都具有相同的 TraceId 。
一個 Span 代表一個操作或一個工作單位。多個 Span 可以是“Trace”的一部分,它代表跨多個進程/節點的執行路徑(通常是分布式的)。同一軌跡內的 Span 具有相同的 TraceId。
Span 共有屬性:
- TraceId
- SpanId
- Start Time
- End Time
- Status
Span 可選屬性:
- Parent SpanId
- Remote Parent
- Attributes
- Annotations
- Message Events
- Links
Exporters
OpenCensus 是獨立于供應商的,可以通過各種 Exporter 實現將數據上傳到任何后端。盡管OpenCensus 為一些后端服務提供了 API ,但用戶也可以實現自己的 Exporter。
Introspection | 內省
OpenCensus 提供在線儀表板,顯示進程中的診斷數據。這些頁面被稱為 z-pages ,它們有助于了解如何查看來自特定進程的數據,而不必依賴任何度量收集器或分布式跟蹤后端。
創建指標
- 定義指標類型
- 定義顯示方式
Track Metrics 一般需要考慮服務負載(Server Load)、響應時間(Response Time)、誤碼率(Error Rates)等。
import (
"go.opencensus.io/stats"
"go.opencensus.io/tag"
"go.opencensus.io/stats/view"
)
var (
requestCounter *stats.Float64Measure
requestlatency *stats.Float64Measure
codeKey tag.Key
DefaultLatencyDistribution = view.DistributionAggregation{0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000}
)
codeKey, _ = tag.NewKey("banias/keys/code")
requestCounter, _ = stats.Float64("banias/measures/request_count", "Count of HTTP requests processed", stats.UnitNone)
requestlatency, _ = stats.Float64("banias/measures/request_latency", "Latency distribution of HTTP requests", stats.UnitMilliseconds)
view.Subscribe(
&view.View{
Name: "request_count",
Description: "Count of HTTP requests processed",
TagKeys: []tag.Key{codeKey},
Measure: requestCounter,
Aggregation: view.CountAggregation{},
})
view.Subscribe(
&view.View{
Name: "request_latency",
Description: "Latency distribution of HTTP requests",
TagKeys: []tag.Key{codeKey},
Measure: requestlatency,
Aggregation: DefaultLatencyDistribution,
})
view.SetReportingPeriod(1 * time.Second)
收集指標數據
- Call the Record method
// Go Code Example
// 說明:defer 用于資源的釋放,會在函數返回之前進行調用。
// 如果有多個 defer表達式,調用順序類似于棧,越后面的 defer 表達式越先被調用。
func (c *Collector) Collect(ctx *fasthttp.RequestCtx) {
defer func(begin time.Time) {
responseTime := float64(time.Since(begin).Nanoseconds() / 1000)
occtx, _ := tag.New(context.Background(), tag.Insert(codeKey, strconv.Itoa(ctx.Response.StatusCode())), )
stats.Record(occtx, requestCounter.M(1))
stats.Record(occtx, requestlatency.M(responseTime))
}(time.Now())
/*do some stuff */
}
第三方數據接口
OpenCensus 收集和跟蹤的應用指標可以在本地顯示,也可將其發送到第三方分析工具或監控系統實現可視化,例如:
- Prometheus|普羅米修斯
- Stackdriver|適用于 Google Cloud Platform 與 AWS 應用的監控、日志記錄和診斷工具 | 示例
- Zipkin
- OpenCensus Tracing with Uber’s Jaeger project
import (
"go.opencensus.io/exporter/prometheus"
"go.opencensus.io/exporter/stackdriver"
openzipkin "github.com/openzipkin/zipkin-go"
"go.opencensus.io/exporter/zipkin"
"go.opencensus.io/trace"
"go.opencensus.io/stats/view"
)
// Export to Prometheus Monitoring.
Exporter, err := prometheus.NewExporter(prometheus.Options{})
if err != nil {
logger.Error("Error creating prometheus exporter ", zap.Error(err))
}
view.RegisterExporter(pExporter)
// Export to Stackdriver Monitoring.
sExporter, err := stackdriver.NewExporter(stackdriver.Options{ProjectID: config.ProjectID})
if err != nil {
logger.Error("Error creating stackdriver exporter ", zap.Error(err))
}
view.RegisterExporter(sExporter)
// Export to Zipkin Monitoring.
localEndpoint, err := openzipkin.NewEndpoint("service-A", "127.0.1.1:8080")
reporter := http.NewReporter("http://127.0.1.110:9411/api/v2/spans")
defer reporter.Close()
exporter := zipkin.NewExporter(reporter, localEndpoint)
trace.RegisterExporter(exporter)
OpenZipkin 數據可視化示例
- 函數內容為空(微秒級)
- 串行調用函數方法,內容包括網絡訪問和持久化操作(毫秒級)
- 并行調用函數方法(Go routine),內容與上同
- 多服務調用
擴展閱讀:開源架構技術漫談
- DevOps 漫談:基于OpenCensus構建分布式跟蹤系統
- 基于Go語言快速構建一個RESTful API服務
- 基于Kafka構建事件溯源型微服務
- 遠程通信協議:從 CORBA 到 gRPC
- 應用程序開發中的日志管理(Go語言描述)
- 數據可視化(七)Graphite 體系結構詳解
- 動態追蹤技術(一):DTrace 導論
- 動態追蹤技術(二):strace+gdb 溯源 Nginx 內存溢出異常
- 動態追蹤技術(三):Tracing Your Kernel Function!
- 動態追蹤技術(四):基于 Linux bcc/BPF 實現 Go 程序動態追蹤
- 動態追蹤技術(五):Welcome DTrace for Linux
- DevOps 資訊 | LinkedIn 開源 Kafka Monitor
更多精彩內容掃碼關注公眾號:RiboseYim's Blog