使用 OpenTelemetry 統(tǒng)一不同服務(wù)的范圍
php小編小新今天為大家介紹一種強大的工具——OpenTelemetry,它可以幫助開發(fā)者在不同的服務(wù)中實現(xiàn)統(tǒng)一的范圍管理。在現(xiàn)代的分布式系統(tǒng)中,應(yīng)用程序通常由多個微服務(wù)組成,每個微服務(wù)都有自己的日志、指標和追蹤信息。OpenTelemetry提供了一種簡單而強大的方式來集成和管理這些信息,使得開發(fā)者能夠更好地理解和調(diào)試整個系統(tǒng)的性能和行為。無論是在本地開發(fā)環(huán)境還是在生產(chǎn)環(huán)境中,OpenTelemetry都能幫助開發(fā)者更好地理解和優(yōu)化他們的應(yīng)用程序。
問題內(nèi)容我剛剛開始使用 opentelemetry,并為此創(chuàng)建了兩個(微)服務(wù):standard 和 geomap。
最終用戶向standard服務(wù)發(fā)送請求,后者又向geomap發(fā)送請求以獲取信息,然后再將結(jié)果返回給最終用戶。我使用 gr電腦端 進行所有通信。
我已經(jīng)對我的功能進行了這樣的檢測:
對于標準:
type standardservice struct {
pb.unimplementedstandardserviceserver
}
func (s *standardservice) getstandard(ctx context.context, in *pb.getstandardrequest) (*pb.getstandardresponse, error) {
conn, _:= createclient(ctx, geomapsvcaddr)
defer conn1.close()
newctx, span1 := otel.tracer(name).start(ctx, "getstandard")
defer span1.end()
countryinfo, err := pb.newgeomapserviceclient(conn).getcountry(newctx,
&pb.getcountryrequest{
name: in.name,
})
//...
return &pb.getstandardresponse{
standard: standard,
}, nil
}
func createclient(ctx context.context, svcaddr string) (*gr電腦端.clientconn, error) {
return gr電腦端.dialcontext(ctx, svcaddr,
gr電腦端.withtransportcredentials(insecure.newcredentials()),
gr電腦端.withunaryinterceptor(otelgr電腦端.unaryclientinterceptor()),
)
}
對于地理地圖:
type geomapservice struct {
pb.unimplementedgeomapserviceserver
}
func (s *geomapservice) getcountry(ctx context.context, in *pb.getcountryrequest) (*pb.getcountryresponse, error) {
_, span := otel.tracer(name).start(ctx, "getcountry")
defer span.end()
span.setattributes(attribute.string("country", in.name))
span.addevent("retrieving country info")
//...
span.addevent("country info retrieved")
return &pb.getcountryresponse{
country: &country,
}, nil
}
這兩個服務(wù)都配置為將其跨度發(fā)送到 jaeger 后端并共享幾乎相同的主要功能(評論中指出了細微的差異):
const (
name = "mapedia"
service = "geomap" //or standard
environment = "production"
id = 1
)
func tracerProvider(url string) (*tracesdk.TracerProvider, error) {
// Create the Jaeger exporter
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil {
return nil, err
}
tp := tracesdk.NewTracerProvider(
// Always be sure to batch in production.
tracesdk.WithBatcher(exp),
// Record information about this application in a Resource.
tracesdk.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName(service),
attribute.String("environment", environment),
attribute.Int64("ID", id),
)),
)
return tp, nil
}
func main() {
tp, err := tracerProvider("localhost:14268/api/traces")
if err != nil {
log.Fatal(err)
}
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
log.Fatal(err)
}
}()
otel.SetTracerProvider(tp)
listener, err := net.Listen("tcp", ":"+port)
if err != nil {
panic(err)
}
s := gr電腦端.NewServer(
gr電腦端.UnaryInterceptor(otelgr電腦端.UnaryServerInterceptor()),
)
reflection.Register(s)
pb.RegisterGeoMapServiceServer(s, &geomapService{}) // or pb.RegisterStandardServiceServer(s, &standardService{})
if err := s.Serve(listener); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
當(dāng)我查看最終用戶對標準服務(wù)的請求生成的跟蹤時,我可以看到它正如預(yù)期的那樣,調(diào)用其geomap服務(wù):
但是,我沒有看到已添加到子范圍的任何屬性或事件(我在檢測 geomap< 的 getcountry 函數(shù)時添加了一個屬性和 2 個事件/em>)。 然而,我注意到這些屬性在另一個單獨的跟蹤中可用(在 jaeger 中的“geomap”服務(wù)下可用),其跨度 id 與標準服務(wù)中的子跨度完全無關(guān): 現(xiàn)在我期望的是有一個跟蹤,并查看與 標準 范圍內(nèi)的子范圍中的 geomap 相關(guān)的所有屬性/事件。如何從這里得到預(yù)期的結(jié)果? 跨度上下文(包含跟蹤 id 和跨度 id,如“service instrumentation & 中所述)術(shù)語”)應(yīng)該從父跨度傳播到子跨度,以便它們成為同一跟蹤的一部分。 使用 opentelemetry,這通常是通過使用為各種庫(包括 gr電腦端)提供的插件來檢測代碼來自動完成的。 在您的代碼中,您將在 函數(shù)中啟動一個新范圍,然后在發(fā)出 請求時使用該上下文 ()。這是正確的,因為新上下文應(yīng)該包含父跨度的跨度上下文 ()。 您正確使用了 在這里,這應(yīng)該確保上下文正確傳播,但不清楚何時調(diào)用此函數(shù)。如果在調(diào)用 函數(shù)之前調(diào)用它,則用于創(chuàng)建客戶端的上下文將不包含來自 的跨度上下文。 為了進行測試,請嘗試確保在調(diào)用 函數(shù)之后創(chuàng)建客戶端,并且在整個請求中使用相同的上下文。 您可以通過將 直接傳遞給 函數(shù)來完成此操作,如 函數(shù)的修改版本所示: 現(xiàn)在,用于創(chuàng)建客戶端并發(fā)出 請求的上下文將包括來自 的跨度上下文,并且它們應(yīng)作為 jaeger 中同一跟蹤的一部分出現(xiàn)。 (一如既往,請檢查 和 等函數(shù)返回的錯誤,為簡潔起見,此處未顯示)。 此外: 另請檢查您的傳播器:確保您使用相同的上下文傳播器 在這兩個服務(wù)中,最好是 w3c tracecontextpropagator,這是 opentelemetry 中默認的。 您可以按如下方式顯式設(shè)置傳播器: 將以上行添加到兩個服務(wù)中 函數(shù)的開頭。 確保傳遞元數(shù)據(jù):gr電腦端 攔截器應(yīng)自動從請求的元數(shù)據(jù)中注入/提取跟蹤上下文,但要仔細檢查以確保其正常工作。 在 函數(shù)中啟動跨度后,您可以記錄跟蹤 id 和跨度 id: 并在 函數(shù)中執(zhí)行相同的操作: 如果上下文正確傳播,兩個服務(wù)中的跟蹤 id 應(yīng)該匹配。
但是,在您的情況下,傳播似乎無法正常工作。
但問題可能與您的 函數(shù)有關(guān):func createclient(ctx context.context, svcaddr string) (*gr電腦端.clientconn, error) {
return gr電腦端.dialcontext(ctx, svcaddr,
gr電腦端.withtransportcredentials(insecure.newcredentials()),
gr電腦端.withunaryinterceptor(otelgr電腦端.unaryclientinterceptor()),
)
}
func (s *standardservice) getstandard(ctx context.context, in *pb.getstandardrequest) (*pb.getstandardresponse, error) {
newctx, span1 := otel.tracer(name).start(ctx, "getstandard")
defer span1.end()
conn, _:= createclient(newctx, geomapsvcaddr)
defer conn.close()
countryinfo, err := pb.newgeomapserviceclient(conn).getcountry(newctx,
&pb.getcountryrequest{
name: in.name,
})
//...
return &pb.getstandardresponse{
standard: standard,
}, nil
}
otel.settextmappropagator(propagation.tracecontext{})
ctx, span := otel.tracer(name).start(ctx, "getcountry")
sc := trace.spancontextfromcontext(ctx)
log.printf("trace id: %s, span id: %s", sc.traceid(), sc.spanid())
defer span.end()
newCtx, span1 := otel.Tracer(name).Start(ctx, "GetStandard")
sc := trace.SpanContextFromContext(newCtx)
log.Printf("Trace ID: %s, Span ID: %s", sc.TraceID(), sc.SpanID())
defer span1.End()
相關(guān)推薦
-
C語言中如何正確使用scanf函數(shù)處理用戶輸入
標題:C語言中正確使用scanf函數(shù)進行用戶輸入的解析在C語言中,scanf函數(shù)是用來從標準輸入讀取數(shù)據(jù)的常用函數(shù)之一。它可以按照格式化字符串的要求,將用戶輸入的數(shù)據(jù)解析并賦值給變量。本文將介紹如何正
-
示例及解析:C語言中scanf函數(shù)的輸入格式
C語言是一種十分重要的程序設(shè)計語言,很多初學(xué)者在學(xué)習(xí)和使用C語言的過程中,經(jīng)常會用到輸入函數(shù)scanf()。通過scanf()函數(shù),我們可以從用戶處獲取輸入數(shù)據(jù),然后將其存儲到指定的變量中。本文將介紹
-
精通C語言函數(shù):全面解析常用函數(shù)的用法與原理
精通C語言函數(shù):全面解析常用函數(shù)的用法與原理摘要:C語言中的函數(shù)是實現(xiàn)代碼復(fù)用和模塊化的重要工具,也是程序設(shè)計中不可或缺的一部分。本文將全面解析常用函數(shù)的用法與原理,包括函數(shù)的定義、調(diào)用和返回值,以及
-
Python中strip 函數(shù)的用法
Python中strip()函數(shù)的用法及代碼示例Python中的strip()函數(shù)是用來去除字符串開頭和結(jié)尾的指定字符(默認為空格)的函數(shù)。它可以非常方便地處理字符串的處理和整理。本文將介紹strip
-
C語言中scanf函數(shù)的使用方法詳解
C語言中scanf函數(shù)的使用方法詳解及代碼示例C語言是一門廣泛應(yīng)用于各種軟件開發(fā)的編程語言,其中的輸入輸出函數(shù)在編寫程序時起著非常重要的作用。其中,scanf函數(shù)是C語言中用于讀取標準輸入的函數(shù)之一,















