1. 简介
OTLP(OpenTelemetry Protocol)是一个用于传输跟踪和度量数据的开放标准协议。它是由开放遥测项目(OpenTelemetry Project)社区制定的,用于在分布式系统中传递遥测信息,包括跟踪数据(traces)和度量数据(metrics),OTLP提供了一种标准化的格式和通信协议,使得不同的遥测系统可以互操作。这样,开发者可以在不同的系统中选择最适合他们需求的跟踪和度量实现,而不用担心不同系统之间的兼容性问题。
OTLP 主要有两个方面的内容:
-
OTLP Traces: 用于传输分布式追踪数据。包括 Span(跨度)的信息,表示请求在不同服务之间的传递路径,以及每个 Span 的相关上下文信息、标签和事件等。
-
OTLP Metrics: 用于传输度量数据,例如服务的性能指标、资源利用率等。度量数据通常包括时间序列数据,用于监控和评估系统的性能。
OTLP 的设计目标是提供灵活性和可扩展性,以适应各种遥测数据的需求。它支持不同的传输协议,包括 gRPC、HTTP/JSON 等,使得 OTLP 可以在不同的网络环境和应用场景中使用。
2. 原理
OTLP 的原理包括了定义通用的数据模型、采用多种传输格式、使用 gRPC 进行高效的数据传输、以及不断更新的协议版本和兼容性规范,以确保在分布式系统中进行跟踪和度量数据的有效传递和交换。
以下是 OTLP 的主要原理:
-
数据模型:
- OTLP 定义了一种通用的数据模型,包括
Traces
和Metrics
两个核心概念。 Traces
涉及分布式追踪,其中跟踪数据由Span
组成,每个Span
表示一个操作或事件。Span
包含了时间戳、持续时间、标签(Tags)、事件(Logs)等信息。Metrics
包括度量信息,如计数器、直方图等。
- OTLP 定义了一种通用的数据模型,包括
-
数据传输格式:
- OTLP 支持多种传输格式,包括 Protocol Buffers(gRPC)和 HTTP/JSON。这种灵活性使得 OTLP 可以适应不同的网络环境和使用场景。
- 在 gRPC 中,使用 Protocol Buffers 定义了数据的结构和序列化方式,提供了高效的二进制传输。
-
gRPC 协议:
- OTLP 使用 gRPC 作为其底层的传输协议。gRPC 是一种高性能的开源 RPC(Remote Procedure Call)框架,它使用 HTTP/2 作为传输协议,并支持双向流(bidirectional streaming)。
- 使用 gRPC 有助于实现高效的通信和低延迟的传输。
-
数据传输流程:
- 发送方(如一个应用程序或服务)通过 OTLP 接口将跟踪和度量数据封装成 OTLP 消息。
- OTLP 消息可以包含一个或多个
Spans
或Metrics
。 - 使用 gRPC 进行数据传输,将 OTLP 消息通过 gRPC 请求发送到接收方(通常是一个遥测系统的代理或收集器)。
- 接收方解析 gRPC 请求,提取 OTLP 消息,然后将数据进行后续处理(存储、展示、分析等)。
-
协议版本和兼容性:
- OTLP 定期发布新的协议版本,以适应遥测领域的发展和需求变化。
- 各个 OTLP 实现应该遵循兼容性规范,以确保不同版本的 OTLP 之间可以正确交换数据。
3. 使用示例
以下以http的方式为例,介绍了otlp的一种使用示例。
package main
import (
"context"
"fmt"
"os"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.21.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("hello1").Start(ctx, "two")
defer span.End()
fibonacci, err := Fibonacci(20)
if err != nil {
return
}
fmt.Println(fibonacci)
}
func newExporter(url string) (*otlptrace.Exporter, error) {
return otlptracegrpc.New(
context.Background(),
otlptracegrpc.WithEndpoint("localhost:4317"),
//otlptracegrpc.WithURLPath("/api/traces"),
otlptracegrpc.WithInsecure(),
)
}
func newResource() (*resource.Resource, error) {
r, err := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("fibqqqq"),
semconv.ServiceVersionKey.String("v0.1.0"),
attribute.String("environment", "demo"),
),
)
if err != nil {
return nil, err
}
return r, nil
}
// 主函数
func main() {
url := "127.0.0.1:4317"
os.Setenv("OTEL_SERVICE_NAME", "t2")
exp, err := newExporter(url)
if err != nil {
fmt.Println("Error creating exporter:", err)
return
}
resource, err := newResource()
if err != nil {
fmt.Println("Error creating resource:", err)
return
}
tp := trace.NewTracerProvider(
trace.WithBatcher(exp),
trace.WithResource(resource),
)
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
fmt.Println(err)
}
}()
otel.SetTracerProvider(tp)
newCtx, span := otel.Tracer("hello").Start(context.Background(), "one")
Run(newCtx)
span.End()
}