Posts 试用 Opentelemetry Trace
Post
Cancel

试用 Opentelemetry Trace

概念介绍

OpenTelemetry

OpenTelemetry是一个由CNCF(Cloud Native Computing Foundation)支持的可观测性框架和工具包,旨在创建和管理遥测数据,如链路(traces)、指标(metrics)和日志(logs)。 OpenTelemetry提供了一套API、SDK、工具和集成方法,以帮助开发者和运维团队收集和分析遥测数据,从而提高分布式系统的透明度和可观测性。 它是一个供应商和工具无关的平台,意味着可以与各种可观测性后端一起使用,包括开源工具如Jaeger和Prometheus,以及商业化产品。

Trace 的概念

在分布式追踪中,Trace是表示一个完整的请求处理流程,它由多个Span组成,每个Span代表请求在单个服务中的处理过程。 Span是Trace的基本工作单位,可以嵌套,通过父级Span ID来表示子操作。 这样,通过Span之间的关系,可以构建出一个完整的Trace,从而追踪整个请求的处理路径。 Trace和Span共同帮助开发者理解分布式系统中请求的流动和延迟情况。

Zipkin 的概念和运行方式

Zipkin是一个分布式追踪系统,它收集解决服务架构中的延迟问题所需的计时数据,并提供数据的收集和查找功能。 核心概念包括Trace、Span、Trace ID、Annotations、Binary Annotations、Collector以及Query and Visualization。 Zipkin通过一个全局唯一的Trace ID将不同的Span关联到同一个Trace上,从而构建起整个请求的调用链路。

Zipkin的工作流程包括:在用户发起调用时,Zipkin客户端为整条调用链路生成一个全局唯一的Trace ID,并为每次分布式调用生成一个Span ID。一个Trace由一组Span组成,可以看作是以Trace为根节点,Span为若干子节点的树。 通过这种方式,Zipkin能够详细记录和展示服务间的调用链路,帮助开发者定位性能瓶颈和故障点

Jaegar

Jaeger是一个开源的分布式跟踪系统,由Uber Technologies开发并贡献给云原生计算基金会(CNCF)。它受到Dapper和OpenZipkin的启发,兼容OpenTracing以及Zipkin追踪格式。Jaeger主要用于监控和诊断基于微服务的分布式系统,包括分布式上下文传播、分布式事务监控、根本原因分析、服务依赖性分析以及性能/延迟优化等功能

Grafana

Grafana是一个开源的度量分析和可视化套件,它提供了一个功能丰富的仪表板,用于展示和监控各种指标和日志。Grafana支持多种数据源,包括但不限于Prometheus、InfluxDB、Elasticsearch、MySQL、PostgreSQL等,使其能够广泛应用于不同的监控场景。

安装和配置

1. 安装 Zipkin 以进行可视化

Zipkin 可以通过 Docker 快速安装

步骤 1: 运行 Zipkin 容器

在您的终端中运行以下命令,以在后台启动 Zipkin 容器,并将其端口映射到本地的 9411 端口。

1
docker run -d -p 9411:9411 openzipkin/zipkin

步骤 2: 访问 Zipkin Dashboard

启动 Zipkin 后,您可以通过访问以下 URL 来查看 Zipkin 的仪表板: http://localhost:9411/

2. 安装 Jaeger

Jaeger 是一个开源的端到端分布式追踪系统,它包括 Zipkin 兼容的 UI,所以如果没有安装 Zipkin 的话也可以直接安装 Jaegar 全家桶。以下是如何安装 Jaeger 的步骤。

步骤 1: 安装 Jaeger-all-in-one

Jaeger 提供了一个 all-in-one 安装包,包括所有必需的组件。您可以通过以下命令安装 Jaeger:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker run --rm --name jaeger \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 14250:14250 \
  -p 14268:14268 \
  -p 14269:14269 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.62.0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
### 步骤 2: 访问 Jaeger Dashboard

安装 Jaeger 后,您可以通过以下 URL 访问其仪表板: [http://localhost:16686/](http://localhost:16686/)

## 3. 安装 Grafana

Grafana 是一个跨平台的开源分析和监控解决方案,它可以用来可视化追踪数据。对于目前很多数据呈现都通过 Grafana 展示 Prometheus,那么对于 Trace 的数据,只要在 Grafana 里面增加一个数据源就可以获得Trace 的视觉化图标。

### 步骤 1: 更新 Homebrew

如果您使用的是 macOS,首先更新您的 Homebrew:

```bash
brew update

步骤 2: 安装 Grafana

使用 Homebrew 安装 Grafana:

1
brew install grafana

步骤 3: 重置管理员密码

重置 Grafana 的管理员密码:

1
/usr/local/bin/grafana cli --config /usr/local/etc/grafana/grafana.ini --homepath /usr/local/share/grafana --configOverrides cfg:default.paths.data=/usr/local/share/grafana admin reset-admin-password xxx

步骤 4: 启动 Grafana 服务

启动 Grafana 服务:

1
brew services start grafana

步骤 5: 访问 Grafana Dashboard

启动 Grafana 后,您可以通过以下 URL 访问其仪表板:http://localhost:3000/

步骤 6: 配置数据源

在 Grafana 中,您需要将数据源配置为 Jaeger。这将允许您在 Grafana 中查看和分析追踪数据。

代码和配置

外部环境都好了,接下来就可以写带有 Trace 的代码

Maven注册依赖

pom.xml 中需要添加针对 opentelemetry api 和 zipkin exporter 的依赖

1
2
3
4
5
6
7
8
<dependency>  
    <groupId>io.opentelemetry</groupId>  
    <artifactId>opentelemetry-api</artifactId>  
</dependency>  
<dependency>  
    <groupId>io.opentelemetry</groupId>  
    <artifactId>opentelemetry-exporter-zipkin</artifactId>  
</dependency>

样例代码

ExampleConfiguration

定义一个配置类,用来初始化 TraceProvider 和 Zipkin Exporter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public final class ExampleConfiguration {  
  
  // Name of the service  
  private static final String SERVICE_NAME = "opentelemetry-trace-demo";  
  
  /** Adds a SimpleSpanProcessor initialized with ZipkinSpanExporter to the TracerSdkProvider */  
  static OpenTelemetry initializeOpenTelemetry(String ip, int port) {  
    String endpoint = String.format("http://%s:%s/api/v2/spans", ip, port);  
    ZipkinSpanExporter zipkinExporter = ZipkinSpanExporter.builder().setEndpoint(endpoint).build();  
  
    Resource serviceNameResource =  
        Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, SERVICE_NAME));  
  
    // Set to process the spans by the Zipkin Exporter  
    SdkTracerProvider tracerProvider =  
        SdkTracerProvider.builder()  
            .addSpanProcessor(SimpleSpanProcessor.create(zipkinExporter))  
            .setResource(Resource.getDefault().merge(serviceNameResource))  
            .build();  
    OpenTelemetrySdk openTelemetry =  
        OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).buildAndRegisterGlobal();  
  
    // add a shutdown hook to shut down the SDK  
    Runtime.getRuntime().addShutdownHook(new Thread(tracerProvider::close));  
  
    // return the configured instance so it can be used for instrumentation.  
    return openTelemetry;  
  }  
}

然后就可以写 main 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public static void main(String[] args) {  
    // Parsing the input  
    if (args.length < 2) {  
        System.out.println("Missing [hostname] [port]");  
        System.exit(1);  
    }  
  
    String ip = args[0];  
    int port = Integer.parseInt(args[1]);  
  
    // it is important to initialize the OpenTelemetry SDK as early as possible in your process.  
    OpenTelemetry openTelemetry = ExampleConfiguration.initializeOpenTelemetry(ip, port);  
    TracerProvider tracerProvider = openTelemetry.getTracerProvider();  
  
    try {  
        ZipkinExample example = new ZipkinExample(tracerProvider);  
        example.demoTrace();  
    } catch (InterruptedException e) {  
        throw new RuntimeException(e);  
    }  
  
}

public void demoTrace() throws InterruptedException {  
    Span span = tracer.spanBuilder("Demo Trace").startSpan();  
  
    try (Scope scope = span.makeCurrent()) {  
  
        methodA();  
        methodB();  
        methodC();  
  
  
    } catch (RuntimeException | InterruptedException e) {  
        span.recordException(e);  
        throw e;  
    } finally {  
        span.end();  
    }  
  
}

在每个具体的 methodA(), methodB(), 以及 methodC()中, 可以继续创建 Span,这些新创建的 Span会自动挂在最外层 Span 的下面。 从 JSON 内容上可以很清楚的看到这个关系。 另外 Trace 提供了一些方法用于给每个 Span 添加更多的信息,例如:

methodASpan.setAttribute("user", "SpiderMan"); - 来给方法添加一个用户属性

methodASpan.setStatus(StatusCode.ERROR, "Data Not Found"); - 定义这个 Span 的状态,这个状态可以被吸收到外层 Span 来作为整个 Span 的成功失败状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
"traceID": "55e00ba1ddb5f27b12e1d16aa2327e29",
            "spans": [
                {
                    "traceID": "55e00ba1ddb5f27b12e1d16aa2327e29",
                    "spanID": "5f424cbffc13d841",
                    "operationName": "method A",
                    "references": [
                        {
                            "refType": "CHILD_OF",
                            "traceID": "55e00ba1ddb5f27b12e1d16aa2327e29",
                            "spanID": "8b562110e2f7d918"
                        }
                    ],
                    "startTime": 1730876891727723,
                    "duration": 2007788,
                    "tags": [
                        {
                            "key": "http.method",
                            "type": "string",
                            "value": "GET"
                        },
                        {
                            "key": "internal.span.format",
                            "type": "string",
                            "value": "zipkin"
                        },
                        {
                            "key": "net.host.ip",
                            "type": "string",
                            "value": "10.140.217.177"
                        },
                        {
                            "key": "otel.scope.name",
                            "type": "string",
                            "value": "io.opentelemetry.example.ZipkinExample"
                        },
                        {
                            "key": "otel.scope.name",
                            "type": "string",
                            "value": "io.opentelemetry.example.ZipkinExample"
                        },
                        {
                            "key": "user",
                            "type": "string",
                            "value": "IronMan"
                        }
                    ],
                    "logs": [],
                    "processID": "p1",
                    "warnings": null
                },

参考

This post is licensed under CC BY 4.0 by the author.

Recent Update

    Trending Tags

    Contents

    Trending Tags