首頁 > 軟體

OpenTelemetry-go的SDK使用方法詳解

2022-09-26 14:02:58

2019年5月,OpenCensus 和 OpenTracing形成了 OpenTelemetry(簡稱 OTel)

也就是說,我們在使用鏈路追蹤SDK的時候就需要使用OpenTelemetry的新規範.OpenTelemetry幫我們實現了相應語言的SDK,所以我們只需要進行呼叫即可.
接下來,我們開始對go所對應的SDK進行使用.

本文主要根據官方檔案範例進行講解.

例子

本文簡化了官方的例子,每個地方都已註釋完畢! 例子使用jaeger作為鏈路追蹤伺服器.

首先我們先引入它的包.

# 安裝核心模組
go get go.opentelemetry.io/otel 
       go.opentelemetry.io/otel/trace
# 安裝匯出器,這裡匯出器使用的是stdout
go get go.opentelemetry.io/otel/sdk 
         go.opentelemetry.io/otel/exporters/stdout/stdouttrace
# 因為我們需要使用jaeger所以要匯入jaeger匯出器
go get -u go.opentelemetry.io/otel/exporters/jaeger

當我們匯入上面的包之後,就可以執行例子了.

package main
import (
	"context"
	"fmt"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/jaeger"
	"go.opentelemetry.io/otel/sdk/resource"
	"go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
)
// 計算斐波那契佇列
func Fibonacci(n uint) (uint64, error) {
	if n <= 1 {
		return uint64(n), nil
	}

	var n2, n1 uint64 = 0, 1
	for i := uint(2); i < n; i++ {
		n2, n1 = n1, n1+n2
	}

	return n2 + n1, nil
}
// 這裡模擬服務之間的掉用的效果.
func Run(ctx context.Context) {
	_, span := otel.Tracer("nihao").Start(ctx, "two")
	defer span.End()
	fibonacci, err := Fibonacci(20)
	if err != nil {
		return
	}
	fmt.Println(fibonacci)
}
// newExporter returns a console exporter.
// 建立匯出器,對於鏈路追蹤伺服器的選擇就是在這裡設定
// 下面是基於jaeger的匯出器.
// 這裡需要注意,在使用jaeger匯出器的時候傳輸格式是jaeger.thrift over HTTP
// 所以在設定url時請使用14268埠 與/api/traces
func newExporter(url string) (*jaeger.Exporter, error) {
	return jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
}

// 資源是一種特殊型別的屬性,適用於程序生成的所有跨度。這些應該用於表示有關非臨時程序的底層後設資料
// 例如,程序的主機名或其範例 ID
func newResource() *resource.Resource {
	r, _ := resource.Merge(
		resource.Default(),
		resource.NewWithAttributes(
			semconv.SchemaURL,
			semconv.ServiceNameKey.String("fib"),
			semconv.ServiceVersionKey.String("v0.1.0"),
			attribute.String("environment", "demo"),
		),
	)
	return r
}

// 主函數
func main() {
	url := "http://collector.tl.com:31498/api/traces"
	// 建立匯出器
	exp, _ := newExporter(url)
	// 建立鏈路生成器,這裡將匯出器與資源資訊設定進去.
	tp := trace.NewTracerProvider(
		trace.WithBatcher(exp),
		trace.WithResource(newResource()),
	)
	// 結束時關閉鏈路生成器
	defer func() {
		if err := tp.Shutdown(context.Background()); err != nil {
			fmt.Println(err)
		}
	}()

	// 將建立的生成器設定為全域性變數.
	// 這樣直接使用otel.Tracer就可以建立鏈路.
	// 否則 就要使用 tp.Tracer的形式建立鏈路.
	otel.SetTracerProvider(tp)
	newCtx, span := otel.Tracer("nihao").Start(context.Background(), "one")
	Run(newCtx)
	span.End()
}

原理

執行成功了,那麼它的底層原理是怎麼實現的,每個元件是怎麼組織的?
這裡我將根據上面的例子畫一張流程圖來進行說明.

通過上面的圖我們可以知道,跨度之間的傳輸使用的是Context 來實現的.
使用span.end()就是將當前跨度資訊傳送給鏈路追蹤伺服器.

方法使用

接下來讓我們根據上面的例子講解一下他們每個方法的使用方式與設定吧!

newExporter

它的作用是建立一個匯出器
在裡面有兩種設定

// 設定jaeger伺服器
jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url))
// 引數是一個options ...CollectorEndpointOption所以讓我們看一下都有哪些設定
jaeger.WithEndpoint(endpoint string) // 設定伺服器地址
jaeger.WithUsername(username string) // 設定使用者名稱
jaeger.WithPassword(password string) // 設定密碼
jaeger.WithHTTPClient(client *http.Client) //設定用於傳送給jeager伺服器所用到的client,有預設.這裡可以設定

// 設定jaeger-agent 代理地址
jaeger.WithAgentEndpoint()
// 引數是options ...AgentEndpointOption
jaeger.WithAgentHost(host string) // 設定代理host
jaeger.WithAgentPort(port string) // 設定代理埠
jaeger.WithLogger(logger *log.Logger) // 設定紀錄檔系統
jaeger.WithDisableAttemptReconnecting() // 設定禁止重新連線UDP
jaeger.WithAttemptReconnectingInterval(interval time.Duration) // 設定代理解析jaeger伺服器之間的時間間隔
jaeger.WithMaxPacketSize(size int) // 設定UDP傳輸的最大包

無論是使用agent還是Collector,匯出器都 使用batchUploader,也就是批次上傳策略.

newResource

存在於每個span中,在jaegerUI上顯示的是tag下面的Process
它有預設設定,也可以自定義,下面讓我們看一下具體使用

// merge 只是合併下面的兩組資源.
r, _ := resource.Merge(
        // 設定預設的資源. 程序id等
		resource.Default(),
        // 設定自定義屬性
		resource.NewWithAttributes(
			semconv.SchemaURL,
			semconv.ServiceNameKey.String("fib"),
			semconv.ServiceVersionKey.String("v0.1.0"),
            // 我們可以使用該方法,來進行新增自定義屬性
			attribute.String("environment", "demo"),
		),
	)

這裡並不強制使用resource.NewWithAttributes,只要返回值是 resource.Resource 即可

trace.NewTracerProvider

建立一個鏈路生成器,我們主要使用它來進行鏈路追蹤

	// 建立生成器,這裡設定了資源與 匯出器.
tp := trace.NewTracerProvider(
		trace.WithBatcher(exp),
		trace.WithResource(newResource()),
	)
// 下面讓我們看一下它都有哪些設定
// 這裡建立的是批次處理匯出器,也就是我們常用的
WithBatcher(e SpanExporter, opts ...BatchSpanProcessorOption)
// 這裡可以新增別的匯出器,也就是說可以同時傳送給jaeger與zipkin
WithSpanProcessor(sp SpanProcessor)
// 設定資源
WithResource(r *resource.Resource)
// Tracers id與span id生成器,預設為亂數
WithIDGenerator(g IDGenerator)
// 設定取樣數
WithSampler(s Sampler)
// 設定屬性,資源的數量
WithSpanLimits(sl SpanLimits)
WithRawSpanLimits(limits SpanLimits)

這裡重點講解一下SpanProcessor.
SpanProcessor是一個匯出器策略,儲存著如何傳送span到鏈路伺服器上,有兩種方式批次處理和立即傳送。
我們上面說的WithCollectorEndpoint 與 WithAgentEndpoint 都是建立的BatchSpanProcessor 也就是批次處理匯出器策略.還存在一個SpanProcessor為simpleSpanProcessor,這個就是立刻傳送,沒有批次處理操作.
我們新增其他匯出器以及策略的方式:
如果使用BatchSpanProcessor 那麼在建立TracerProvider時的傳參使用trace.WithBatcher即可
如果想自定義,則使用WithSpanProcessor
比如使用simpleSpanProcessor,則設定為 WithSpanProcessor(jaeger.NewSimpleSpanProcessor(exporter SpanExporter))

沒錯,想要新增一個匯出器就需要設定匯出策略。所以匯出器與匯出策略時繫結的。

otel

otel是一個全域性設定元件,我們可以將生成器設定為全域性屬性,方便呼叫。

otel.SetTracerProvider(tp)

注意

具體的span使用請存取
下文都是拷貝官網.

獲取當前跨度

通過上面我們知道,對當前跨度的操作是使用span就可以了,但是在日常過程中我們不能傳輸兩個值一個Context和span(通常只傳輸Context),所以可以使用Context逆向解析出當前的span.

ctx := context.TODO()
span := trace.SpanFromContext(ctx)

:::success
下面所有設定都會在每個span的log中展示出來,只是為了更能標識資訊,沒有實質性的用處.(比如設定even,並不是執行這個事件,只是輸出一個key為even value 為msg的資訊)
:::

設定span狀態

span.SetStatus(codes.Error, "operationThatCouldFail failed")

設定span屬性

span.SetAttributes(attribute.Bool("isTrue", true), attribute.String("stringAttr", "hi!"))

記錄錯誤

span.RecordError(err)

設定活動

span.AddEvent("Acquiring lock")

tp.Shutdown

注意在使用完後已經要呼叫Shutdown,它的功能是關閉使用者端與鏈路伺服器之間的連線,也就是我們為兩個服務建立連線後為了防止資源浪費所以會呼叫 con.close() 即使關閉.

defer func() {
		if err := tp.Shutdown(context.Background()); err != nil {
			fmt.Println(err)
		}
	}()

到此這篇關於OpenTelemetry-go的SDK使用方法的文章就介紹到這了,更多相關OpenTelemetry-go的sdk內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com