探索 Go 語(yǔ)言中 Opentelemetry 與 Prometheus 集成,導(dǎo)出 HTTP 服務(wù)指標(biāo)監(jiān)控,并最終將 Prometheus 指標(biāo)可視化到 Grafana 中。
分布式鏈路跟蹤(
Distributed Tracing
)的概念最早是由 Google 提出來(lái)的,發(fā)展至今技術(shù)已經(jīng)比較成熟,也是有一些協(xié)議標(biāo)準(zhǔn)可以參考。目前在
Tracing
技術(shù)這塊比較有影響力的是兩大開(kāi)源技術(shù)框架:Netflix 公司開(kāi)源的
OpenTracing
和 Google 開(kāi)源的
OpenCensus
。兩大框架都擁有比較高的開(kāi)發(fā)者群體。為形成統(tǒng)一的技術(shù)標(biāo)準(zhǔn),兩大框架最終磨合成立了
OpenTelemetry
項(xiàng)目,簡(jiǎn)稱
otel
。otel 有鏈路追蹤和監(jiān)控告警兩大塊,關(guān)于監(jiān)控告警,可以查看另一篇文章:
Go 鏈路追蹤入門 Opentelemetry
Prometheus
源自 SoundCloud,擁有一整套開(kāi)源系統(tǒng)監(jiān)控和警報(bào)工具包,是支持
OpenTelemetry
的系統(tǒng)之一,是
CNCF
的第二個(gè)項(xiàng)目。
Grafana 是一個(gè)開(kāi)源的分析和可視化平臺(tái),它允許你查詢、可視化和警報(bào)來(lái)自各種數(shù)據(jù)源的數(shù)據(jù)。它提供了一個(gè)用戶友好的界面,用于創(chuàng)建和共享儀表板、圖表和警報(bào)。Grafana 支持廣泛的數(shù)據(jù)源,其中就包括
Prometheus
這里為了簡(jiǎn)單入門,盡量簡(jiǎn)單的介紹一些抽象概念,結(jié)合著代碼理解,如果不能理解也沒(méi)關(guān)系,代碼寫著寫著自然就明白了:
Meter Provider
用于接口化管理全局的
Meter
創(chuàng)建,相當(dāng)于全局的監(jiān)控指標(biāo)管理工廠。
Meter
用于接口化創(chuàng)建并管理全局的
Instrument
,不同的
Meter
可以看做是不同的程序組件。
Instrument
用于管理不同組件下的各個(gè)不同類型的指標(biāo),例如
http.server.request.total
Measurements
對(duì)應(yīng)指標(biāo)上報(bào)的具體的
DataPoint
指標(biāo)數(shù)據(jù),是一系列的數(shù)值項(xiàng)。
Metric Reader
用于實(shí)現(xiàn)對(duì)指標(biāo)的數(shù)據(jù)流讀取,內(nèi)部定義了具體操作指標(biāo)的數(shù)據(jù)結(jié)構(gòu)。
OpenTelemetry
官方社區(qū)提供了多種靈活的
Reader
實(shí)現(xiàn),例如
PeridRader、ManualReader
等。
Metric Exporter
Exporter
用于暴露本地指標(biāo)到對(duì)應(yīng)的第三方廠商,例如:
Promtheus、Zipkin
等。
OpenTelemetry metrics
有許多不同指標(biāo)類型,可以把它想象成類似于
int, float
這種的變量類型:
Counter:
只增不減的指標(biāo),比如
http
請(qǐng)求總數(shù),字節(jié)大。
Asynchronous Counter: 異步 Counter;
UpDownCounter:
可增可減的指標(biāo),比如
http
活動(dòng)連接數(shù);
Asynchronous UpDownCounter: 異步 Counter;
Gauge:
可增可減的指標(biāo),瞬時(shí)計(jì)量的值,比如
CPU
使用,它是異步的;
Histogram :分組聚合指標(biāo),這個(gè)較為難以理解一些,可以移步 此處 查看,當(dāng)然,后文也會(huì)有一個(gè)詳細(xì)的例子來(lái)使用它。
廢話了一堆,終于可以實(shí)戰(zhàn)了。我們先以
http
請(qǐng)求總數(shù)為例來(lái)走一遍整個(gè)采集指標(biāo)流程。安裝擴(kuò)展:
go get github.com/prometheus/client_golang
go get go.opentelemetry.io/otel/exporters/prometheus
go get go.opentelemetry.io/otel/metric
go get go.opentelemetry.io/otel/sdk/metric
打開(kāi)
main.go
,編寫以下代碼:
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/otel/exporters/prometheus"
api "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/sdk/metric"
)
const meterName = "oldme_prometheus_testing"
var (
requestHelloCounter api.Int64Counter
)
func main() {
ctx := context.Background()
// 創(chuàng)建 prometheus 導(dǎo)出器
exporter, err := prometheus.New()
if err != nil {
log.Fatal(err)
}
// 創(chuàng)建 meter
provider := metric.NewMeterProvider(metric.WithReader(exporter))
meter := provider.Meter(meterName)
// 創(chuàng)建 counter 指標(biāo)類型
requestHelloCounter, err = meter.Int64Counter("requests_hello_total")
if err != nil {
log.Fatal(err)
}
go serveMetrics()
ctx, _ = signal.NotifyContext(ctx, os.Interrupt)
<-ctx.Done()
}
func serveMetrics() {
log.Printf("serving metrics at localhost:2223/metrics")
http.Handle("/metrics", promhttp.Handler())
http.Handle("/index", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 記錄 counter 指標(biāo)
requestHelloCounter.Add(r.Context(), 1)
_, _ = w.Write([]byte("Hello, Otel!"))
}))
err := http.ListenAndServe(":2223", nil) //nolint:gosec // Ignoring G114: Use of net/http serve function that has no support for setting timeouts.
if err != nil {
fmt.Printf("error serving http: %v", err)
return
}
}
在我們的代碼中,我們定義一個(gè)名字為
requests_hello_total
的
Int64Counter
指標(biāo)類型,
Int64Counter
代表這是一個(gè)只增不減的
int64
數(shù)值,用作記錄請(qǐng)求總數(shù)正好合適。運(yùn)行我們的程序,如果不出錯(cuò)的話,訪問(wèn)
http://localhost:2223/index
可以看到
Hello, Otel!
。并且我們?cè)L問(wèn)
http://localhost:2223/metrics
可以看到指標(biāo)數(shù)據(jù):
這里數(shù)據(jù)還沒(méi)有進(jìn)行可視化,我們先把流程走通,多訪問(wèn)幾次
http://localhost:2223/index
可以看到
requests_hello_total
會(huì)增加:
接下來(lái)我們采集一下
Histogram
指標(biāo),統(tǒng)計(jì)在
0.1, 0.2, 0.5, 1, 2, 5
秒以內(nèi)的
http
請(qǐng)求數(shù),在
main.go
中加上相關(guān)代碼,可以直接復(fù)制過(guò)去:
package main
import (
"context"
"fmt"
"log"
"math/rand"
"net/http"
"os"
"os/signal"
"time"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/otel/exporters/prometheus"
api "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/sdk/metric"
)
const meterName = "oldme_prometheus_testing"
var (
requestHelloCounter api.Int64Counter
requestDurationHistogram api.Float64Histogram
)
func main() {
ctx := context.Background()
// 創(chuàng)建 prometheus 導(dǎo)出器
exporter, err := prometheus.New()
if err != nil {
log.Fatal(err)
}
// 創(chuàng)建 meter
provider := metric.NewMeterProvider(metric.WithReader(exporter))
meter := provider.Meter(meterName)
// 創(chuàng)建 counter 指標(biāo)類型
requestHelloCounter, err = meter.Int64Counter("requests_hello_total")
if err != nil {
log.Fatal(err)
}
// 創(chuàng)建 Histogram 指標(biāo)類型
requestDurationHistogram, err = meter.Float64Histogram(
"request_hello_duration_seconds",
api.WithDescription("記錄 Hello 請(qǐng)求的耗時(shí)統(tǒng)計(jì)"),
api.WithExplicitBucketBoundaries(0.1, 0.2, 0.5, 1, 2, 5),
)
if err != nil {
log.Fatal(err)
}
go serveMetrics()
go goroutineMock()
ctx, _ = signal.NotifyContext(ctx, os.Interrupt)
<-ctx.Done()
}
func serveMetrics() {
log.Printf("serving metrics at localhost:2223/metrics")
http.Handle("/metrics", promhttp.Handler())
http.Handle("/index", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 記錄 counter 指標(biāo)
requestHelloCounter.Add(r.Context(), 1)
// 計(jì)算請(qǐng)求處理時(shí)間
startTime := time.Now()
// 模擬請(qǐng)求處理時(shí)間
time.Sleep(time.Duration(rand.Intn(3)) * time.Second)
defer func() {
duration := time.Since(startTime).Seconds()
requestDurationHistogram.Record(r.Context(), duration)
}()
_, _ = w.Write([]byte("Hello, Otel!"))
}))
err := http.ListenAndServe(":2223", nil) //nolint:gosec // Ignoring G114: Use of net/http serve function that has no support for setting timeouts.
if err != nil {
fmt.Printf("error serving http: %v", err)
return
}
}
// 隨機(jī)模擬若干個(gè)協(xié)程
func goroutineMock() {
for {
go func() {
// 等待若干秒
var s = time.Duration(rand.Intn(10))
time.Sleep(s * time.Second)
}()
time.Sleep(1 * time.Millisecond)
}
}
走到這里,代碼層面結(jié)束了,已經(jīng)成功一半了,代碼開(kāi)源在
Github
。之后我們就可以安裝
Prometheus
服務(wù)端和
Grafana
來(lái)進(jìn)行數(shù)據(jù)可視化。
Prometheus
有多種安裝方式,我這里依舊采用
Docker
安裝,當(dāng)然,你也可以使用其他方式安裝,具體安裝方式可以參考其他文章,后續(xù)
Grafana
同理,不在贅述,在
Prometheus.yml
中填寫
targets
我們的地址:
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:2223"]
Prometheus
會(huì)自動(dòng)去
{{target}}/metrics
中拉取我們的指標(biāo)。之后在瀏覽器打開(kāi)
Promethues
的地址,例如我的是:http://localhost:9090,如果全部正常的話可以在
status:targets
中看見(jiàn)我們的指標(biāo):
在
Promethues
的首頁(yè)查詢
requests_hello_total
指標(biāo)可以看到可視化的圖表:
我的
Grafana
安裝好了,登錄進(jìn)去后是這樣的(我更改后默認(rèn)顏色):
在
Data source
中添加
Prometheus
服務(wù)器,然后在
Dashboard
中添加我們想要監(jiān)控的指標(biāo),即可看到更美觀的圖表:
機(jī)器學(xué)習(xí):神經(jīng)網(wǎng)絡(luò)構(gòu)建(下)
閱讀華為Mate品牌盛典:HarmonyOS NEXT加持下游戲性能得到充分釋放
閱讀實(shí)現(xiàn)對(duì)象集合與DataTable的相互轉(zhuǎn)換
閱讀鴻蒙NEXT元服務(wù):論如何免費(fèi)快速上架作品
閱讀算法與數(shù)據(jù)結(jié)構(gòu) 1 - 模擬
閱讀基于鴻蒙NEXT的血型遺傳計(jì)算器開(kāi)發(fā)案例
閱讀5. Spring Cloud OpenFeign 聲明式 WebService 客戶端的超詳細(xì)使用
閱讀Java代理模式:靜態(tài)代理和動(dòng)態(tài)代理的對(duì)比分析
閱讀Win11筆記本“自動(dòng)管理應(yīng)用的顏色”顯示規(guī)則
閱讀本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請(qǐng)發(fā)郵件[email protected]
湘ICP備2022002427號(hào)-10 湘公網(wǎng)安備:43070202000427號(hào)© 2013~2025 haote.com 好特網(wǎng)