Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Satukan skop perkhidmatan yang berbeza menggunakan OpenTelemetry

Satukan skop perkhidmatan yang berbeza menggunakan OpenTelemetry

WBOY
WBOYke hadapan
2024-02-13 23:00:11439semak imbas

使用 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.

Kandungan soalan

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?

Penyelesaian

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.unaryclientinterceptorotelgrpc.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

, konteks yang digunakan untuk mencipta klien akan

tidak getstandard mengandungi konteks span dari

.

newctx 直接传递给 getcountry 函数来完成此操作,如 getstandardUntuk ujian, cuba pastikan klien dibuat selepas memanggil fungsi

dan konteks yang sama digunakan sepanjang permintaan.

getcountry 请求的上下文将包括来自 getstandardAnda boleh melakukan ini dengan menghantar newctx terus ke fungsi

, seperti yang ditunjukkan dalam versi ubah suai fungsi

: createclientgetcountry

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.
  • (Seperti biasa, semak ralat yang dikembalikan oleh fungsi seperti

    dan , yang tidak ditunjukkan di sini secara ringkas). a> Juga:

    Semak juga propagator anda: pastikan anda menggunakan

    context propagatormain yang sama dalam kedua-dua perkhidmatan, sebaik-baiknya

    w3c tracecontextpropagator
  • , yang merupakan lalai dalam opentelemetry.
  • Anda boleh menetapkan penyebar secara eksplisit seperti berikut:

    otel.settextmappropagator(propagation.tracecontext{})
    

    Tambah baris di atas pada permulaan kedua-dua fungsi perkhidmatan getcountry.

    🎜 🎜🎜Pastikan lulus metadata: Pemintas grpc harus secara automatik menyuntik/mengekstrak konteks penjejakan daripada metadata permintaan, tetapi semak semula untuk memastikan ia berfungsi dengan betul. 🎜 🎜Selepas memulakan span dalam fungsi 🎜, anda boleh log id jejak dan id span: 🎜
    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!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam