在 gRPC Java 中设置基本 OpenTelemetry 插件

1. 简介

在此 Codelab 中,您将使用 gRPC 创建一个客户端和一个服务器,它们将构成用 Java 编写的路线映射应用的基础。

完成本教程后,您将拥有一个简单的 gRPC HelloWorld 应用,该应用已通过 gRPC OpenTelemetry 插件进行插桩,并且您将能够在 Prometheus 中看到导出的可观测性指标。

学习内容

  • 如何为现有的 gRPC Java 应用设置 OpenTelemetry 插件
  • 运行本地 Prometheus 实例
  • 将指标导出到 Prometheus
  • 查看 Prometheus 信息中心内的指标

2. 准备工作

所需条件

  • git
  • curl
  • JDK v8 或更高版本

安装必备项:

sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl

获取代码

为了简化学习过程,此 Codelab 提供了预构建的源代码框架,可帮助您快速入门。以下步骤将指导您在应用中对 gRPC OpenTelemetry 插件进行插桩。

grpc-codelab

此 Codelab 的框架源代码位于此 GitHub 目录中。如果您不想自行实现代码,可以在 completed 目录中找到已完成的源代码。

首先,克隆 grpc Codelab 代码库,然后 cd 进入 grpc-java-opentelemetry 文件夹:

git clone https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-java-opentelemetry/

或者,您也可以下载仅包含 Codelab 目录的 .zip 文件,然后手动将其解压缩。

3. 注册 OpenTelemetry 插件

我们需要一个 gRPC 应用来添加 gRPC OpenTelemetry 插件。在此 Codelab 中,我们将使用简单的 gRPC HelloWorld 客户端和服务器,并使用 gRPC OpenTelemetry 插件对其进行插桩处理。

第一步是在客户端中注册配置了 Prometheus 导出器的 OpenTelemetry 插件。使用您最喜爱的编辑器打开 codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryClient.java,然后修改 main 以添加代码来设置 gRPC Java OpenTelemetry API。

在客户端上设置插桩

创建 Prometheus 导出器

创建 PrometheusHttpServer 以将 OpenTelemetry 指标转换为 Prometheus 格式,并通过 HttpServer 公开这些指标。以下代码段会创建一个新的 Prometheus Exporter

// Default prometheus port i.e `prometheusPort` has been initialized to 9465
 
PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
        .setPort(prometheusPort)         
        .build();

创建 OpenTelemetry SDK 实例

将上述创建 prometheusExporter 注册为 MetricReader,以从 SdkMeterProvider 读取指标。SdkMeterProvider 用于配置指标设置。

SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
        .registerMetricReader(prometheusExporter)
        .build();

使用上面创建的 sdkMeterProvider 为 OpenTelemetry 的 SDK 实现创建一个 OpenTelemetrySdk 实例。

OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
        .setMeterProvider(sdkMeterProvider)
        .build();

创建 GrpcOpenTelemetry 实例

使用 GrpcOpenTelemetry API 设置使用 Prometheus 指标导出器的 OpenTelemetry SDK。

GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
        .sdk(openTelemetrySdk)
        .build();

// Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();

使用 registerGlobal 全局注册 GrpcOpenTelemetry 实例后,所有后续创建的 gRPC 客户端和服务器都将通过 OpenTelemetry 进行检测。

关闭 OpenTelemetry SDK

关闭需要在 ShutDownHook 内进行。openTelemetrySdk.close() 会关闭 SDK,还会对 SdkMeterProvider 调用关闭。

在服务器上设置插桩

同样,我们也将 GrpcOpenTelemetry 添加到服务器。打开 codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryServer.java 并添加代码以初始化 GrpcOpenTelemetry。

创建 Prometheus 导出器

由于此 Codelab 可能会在同一台机器上运行,因此我们使用不同的端口来托管 gRPC 服务器端指标,以避免在创建 PrometheusHttpServer 时发生端口冲突。

// Default prometheus port i.e `prometheusPort` has been set to 9464

PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
        .setPort(prometheusPort)
        .build();

创建 OpenTelemetry SDK 实例

SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
        .registerMetricReader(prometheusExporter)
        .build();

使用 OpenTelemetry SDK 初始化 GrpcOpenTelemetry

OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
        .setMeterProvider(sdkMeterProvider)
        .build();

创建 GrpcOpenTelemetry 实例

GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
        .sdk(openTelemetrySdk)
        .build();
    // Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();

关闭 OpenTelemetry SDK

在 gRPC 通道关闭后。调用 openTelemetrySdk.close() 会关闭 SDK,还会对 SdkMeterProvider 调用关闭。

4. 运行示例并查看指标

如需运行服务器,请运行以下命令:

cd start_here
../gradlew installDist
./build/install/start_here/bin/opentelemetry-server

如果设置成功,您将看到以下服务器输出 -

[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryServer start
INFO: Server started, listening on 50051

在服务器运行时,在另一个终端上运行客户端 -

./build/install/start_here/bin/opentelemetry-client world

成功运行后,输出结果如下所示:

[date and time]io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Greeting: Hello world 
[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Will try to greet world ...
[date and time]io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Greeting: Hello world

由于我们已设置 gRPC OpenTelemetry 插件以使用 Prometheus 导出指标。这些指标将分别在 localhost:9464(服务器)和 localhost:9465(客户端)上提供。

如需查看客户端指标,请执行以下操作:

curl localhost:9465/metrics

结果的格式应为:

# HELP grpc_client_attempt_duration_seconds Time taken to complete a client call attempt
# TYPE grpc_client_attempt_duration_seconds histogram
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.002"} 0
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.003"} 2
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.004"} 14
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.005"} 29
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.1"} 33
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="+Inf"} 34
grpc_client_attempt_duration_seconds_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34
grpc_client_attempt_duration_seconds_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 0.46512665300000006
# HELP grpc_client_attempt_rcvd_total_compressed_message_size_bytes Compressed message bytes received per call attempt
# TYPE grpc_client_attempt_rcvd_total_compressed_message_size_bytes histogram
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 442.0
# HELP grpc_client_attempt_sent_total_compressed_message_size_bytes Compressed message bytes sent per client call attempt
# TYPE grpc_client_attempt_sent_total_compressed_message_size_bytes histogram
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="1024.0"} 34
grpc_client_attempt_sent_total_compressed_message_size_bytes_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 238.0
# HELP grpc_client_attempt_started_total Number of client call attempts started
# TYPE grpc_client_attempt_started_total counter
grpc_client_attempt_started_total{grpc_method="helloworld.Greeter/SayHello",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34.0
# HELP grpc_client_call_duration_seconds Time taken by gRPC to complete an RPC from application's perspective
# TYPE grpc_client_call_duration_seconds histogram
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.003"} 2
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="+Inf"} 34
grpc_client_call_duration_seconds_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34
grpc_client_call_duration_seconds_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 0.512708707
# TYPE target_info gauge
target_info{service_name="unknown_service:java",telemetry_sdk_language="java",telemetry_sdk_name="opentelemetry",telemetry_sdk_version="1.40.0"} 1

同样,对于服务器端指标 -

curl localhost:9464/metrics

5. 在 Prometheus 上查看指标

在此示例中,我们将设置一个 Prometheus 实例,该实例将抓取使用 Prometheus 导出指标的 gRPC 示例客户端和服务器。

下载适用于您的平台的最新版 Prometheus,然后将其解压并运行:

tar xvfz prometheus-*.tar.gz
cd prometheus-*

创建一个包含以下内容的 Prometheus 配置文件:

cat > grpc_otel_java_prometheus.yml <<EOF
scrape_configs:
  - job_name: "prometheus"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9090"]
  - job_name: "grpc-otel-java"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9464", "localhost:9465"]
EOF

使用新配置启动 Prometheus -

./prometheus --config.file=grpc_otel_java_prometheus.yml

此命令会将客户端和服务器 Codelab 进程的指标配置为每 5 秒抓取一次。

前往 http://localhost:9090/graph 查看指标。例如,以下查询:

histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))

将显示一个图表,其中包含使用 1 分钟作为分位数计算的时间窗口的尝试延迟时间中位数。

查询率 -

increase(grpc_client_attempt_duration_seconds_bucket[1m])

6. (可选)用户练习

在 Prometheus 信息中心内,您会发现 QPS 较低。看看您能否在示例中找到一些限制 QPS 的可疑代码。

对于热衷于此的开发者,客户端代码限制为在给定时刻只能有一个待处理的 RPC。您可以修改此设置,以便客户端发送更多 RPC,而无需等待之前的 RPC 完成。(此问题的解决方案尚未提供。)