Rumah > Artikel > pembangunan bahagian belakang > Satukan skop perkhidmatan yang berbeza menggunakan OpenTelemetry
Editor PHP, Xiaoxin, hari ini memperkenalkan kepada anda alat yang berkuasa - OpenTelemetry, yang boleh membantu pembangun mencapai pengurusan skop bersatu dalam perkhidmatan yang berbeza. Dalam sistem teragih moden, aplikasi selalunya terdiri daripada berbilang perkhidmatan mikro, masing-masing dengan log, metrik dan maklumat pengesanan sendiri. OpenTelemetry menyediakan cara yang mudah dan berkuasa untuk menyepadukan dan mengurus maklumat ini, membolehkan pembangun memahami dan menyahpepijat prestasi dan tingkah laku keseluruhan sistem dengan lebih baik. Sama ada dalam persekitaran pembangunan tempatan atau dalam persekitaran pengeluaran, OpenTelemetry membantu pembangun lebih memahami dan mengoptimumkan aplikasi mereka.
Saya baru mula menggunakan opentelemetri dan mencipta dua perkhidmatan (mikro) untuknya: standard dan geomap.
Pengguna akhir menghantar permintaan kepada perkhidmatan standard, yang seterusnya menghantar permintaan kepada geomap untuk mendapatkan maklumat, yang seterusnya mengembalikan hasilnya kepada pengguna akhir. Saya menggunakan grpc untuk semua komunikasi.
Saya telah menggunakan fungsi saya seperti ini:
Untuk Standard:
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) (*grpc.clientconn, error) { return grpc.dialcontext(ctx, svcaddr, grpc.withtransportcredentials(insecure.newcredentials()), grpc.withunaryinterceptor(otelgrpc.unaryclientinterceptor()), ) }
Untuk Peta Geo:
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 }
Kedua-dua perkhidmatan dikonfigurasikan untuk menghantar rentang mereka ke bahagian belakang jaeger dan berkongsi fungsi utama yang hampir sama (perbezaan halus dinyatakan dalam ulasan):
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("http://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 := grpc.NewServer( grpc.UnaryInterceptor(otelgrpc.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) } }
Apabila saya melihat jejak yang dijana oleh permintaan pengguna akhir kepada perkhidmatan standard, saya dapat melihat bahawa ia, seperti yang dijangkakan, memanggil perkhidmatan geomapnya:
Walau bagaimanapun, saya tidak melihat sebarang sifat atau acara yang telah ditambahkan pada subjulat (saya menambah satu sifat dan 2 acara apabila saya menggunakan geomapgetcountry function).
Namun, saya perhatikan bahawa sifat ini tersedia dalam jejak berasingan lain (tersedia di bawah perkhidmatan "geomap" dalam jaeger) yang id rentangnya tidak berkaitan sama sekali dengan subspan dalam perkhidmatan standard:
Sekarang apa yang saya harapkan ialah mempunyai jejak dan melihat semua sifat/peristiwa yang berkaitan dengan geomap dalam subskop dalam skop standard. Bagaimanakah saya boleh mendapatkan hasil yang diharapkan dari sini?
Konteks span (mengandungi id jejak dan id span seperti yang diterangkan dalam " instrumentasi perkhidmatan & istilah ") hendaklah disebarkan daripada span induk kepada span anak supaya ia menjadi sebahagian daripada surih yang sama.
Menggunakan opentelemetri, ini biasanya dilakukan secara automatik dengan memperalatkan kod menggunakan pemalam yang disediakan untuk pelbagai perpustakaan, termasuk grpc.
Walau bagaimanapun, penyebaran nampaknya tidak berfungsi dengan betul dalam kes anda.
Dalam kod anda, anda akan mempunyai getstandard
函数中启动一个新范围,然后在发出 getcountry
请求时使用该上下文 (newctx
)。这是正确的,因为新上下文应该包含父跨度的跨度上下文 (getstandard
).
Tetapi masalahnya mungkin berkaitan dengan fungsi createclient
anda:
func createclient(ctx context.context, svcaddr string) (*grpc.clientconn, error) { return grpc.dialcontext(ctx, svcaddr, grpc.withtransportcredentials(insecure.newcredentials()), grpc.withunaryinterceptor(otelgrpc.unaryclientinterceptor()), ) }
Anda menggunakan dengan betul otelgrpc.unaryclientinterceptor
otelgrpc.unaryclientinterceptor
在这里,这应该确保上下文正确传播,但不清楚何时调用此函数。如果在调用 getstandard
函数之前调用它,则用于创建客户端的上下文将不包含来自 getstandard
Di sini, ini harus memastikan bahawa konteks disebarkan dengan betul, tetapi tidak jelas apabila fungsi ini dipanggil. Jika ia dipanggil sebelum memanggil fungsi
tidak getstandard
mengandungi konteks span dari
newctx
直接传递给 getcountry
函数来完成此操作,如 getstandard
Untuk ujian, cuba pastikan klien dibuat selepas memanggil fungsi
getcountry
请求的上下文将包括来自 getstandard
Anda boleh melakukan ini dengan menghantar newctx
terus ke fungsi
: createclient
和 getcountry
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 }Konteks yang digunakan untuk mencipta pelanggan dan membuat permintaan
kini akan merangkumi konteks rentang daripada
dan ia sepatutnya muncul sebagai sebahagian daripada jejak yang sama dalam jaeger.dan , yang tidak ditunjukkan di sini secara ringkas). a> Juga:
Semak juga propagator anda: pastikan anda menggunakancontext propagatormain
yang sama dalam kedua-dua perkhidmatan, sebaik-baiknya
Anda boleh menetapkan penyebar secara eksplisit seperti berikut:
otel.settextmappropagator(propagation.tracecontext{})
Tambah baris di atas pada permulaan kedua-dua fungsi perkhidmatan getcountry
.
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()
并在 getstandard
函数中执行相同的操作:
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()
如果上下文正确传播,两个服务中的跟踪 id 应该匹配。
Atas ialah kandungan terperinci Satukan skop perkhidmatan yang berbeza menggunakan OpenTelemetry. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!