全部產品
Search
文件中心

Application Real-Time Monitoring Service:通過Jaeger上報Java應用資料

更新時間:Aug 07, 2024

通過Jaeger為應用埋點並上報鏈路資料至Managed Service for OpenTelemetry後,Managed Service for OpenTelemetry即可開始監控應用,您可以查看應用拓撲、調用鏈路、異常事務、慢事務和SQL分析等一系列監控資料。本文介紹如何進行手動埋點,以及使用Spring Cloud組件和gRPC組件進行自動埋點。

重要

為獲得更豐富的功能、更先進的鏈路追蹤能力,以及最佳使用體驗,建議您使用OpenTelemetry協議將應用接入Managed Service for OpenTelemetry

我們為您提供了詳細的OpenTelemetry接入指南和最佳實務,協助您快速上手Managed Service for OpenTelemetry。更多資訊,請參見接入應用

前提條件

擷取存取點資訊

  1. 登入ARMS控制台,在左側導覽列單擊接入中心

  2. 服務端應用地區單擊Jaeger卡片。

  3. 在彈出的Jaeger面板中選擇資料需要上報的地區。

    說明

    初次接入的地區將會自動進行資源初始化。

  4. 選擇串連方式上報方式,然後複製存取點資訊。

    • 串連方式:若您的服務部署在阿里雲上,且所屬地區與選擇的接入地區一致,推薦使用阿里雲內網方式,否則選擇公網方式。

    • 上報方式:根據用戶端支援的協議類型選擇HTTP或gRPC協議上報資料。

    image.png

背景資訊

Jaeger是一款開源分布式追蹤系統,相容OpenTracing API,且已加入CNCF開源組織。其主要功能是彙總來自各個異構系統的即時監控資料。目前OpenTracing社區已有許多組件可支援各種Java架構,例如:

要通過Jaeger將Java應用資料上報至Managed Service for OpenTelemetry控制台,首先需要完成埋點工作。您可以手動埋點,也可以利用各種現有外掛程式實現埋點的目的。本文介紹以下三種埋點方法。

  • 手動埋點

  • 通過Spring Cloud組件埋點

  • 通過gRPC組件埋點

資料是如何上報的?

  • 不通過Jaeger Agent而直接上報資料的原理如下圖所示。

  • 通過Jaeger Agent上報資料的原理如下圖所示。

為Java應用手動埋點

要通過Jaeger將Java應用資料上報至可觀測鏈路 OpenTelemetry 版控制台,首先需要完成埋點工作。本樣本為手動埋點。

  1. 下載Demo工程,進入manualDemo目錄,並按照Readme的說明運行程式。

  2. 開啟pom.xml,添加對Jaeger用戶端的依賴。

    <dependency>
        <groupId>io.jaegertracing</groupId>
        <artifactId>jaeger-client</artifactId>
        <version>0.31.0</version>
    </dependency>
  3. 配置初始化參數並建立Tracer對象。

    Tracer對象可以用來建立Span對象以便記錄分布式操作時間、通過Extract/Inject方法跨機器透傳資料、或設定當前Span。Tracer對象還配置了上報資料的網關地址、本機IP地址、採樣率、服務名等資料。您可以通過調整採樣率來減少因上報資料產生的開銷。

    說明

    請將<endpoint>替換成可觀測鏈路 OpenTelemetry 版控制台叢集配置 > 存取點資訊頁面相應用戶端和地區的存取點。擷取存取點資訊的方法,請參見前提條件。

    // 將manualDemo替換為您的應用程式名稱。
    io.jaegertracing.Configuration config = new io.jaegertracing.Configuration("manualDemo");
    io.jaegertracing.Configuration.SenderConfiguration sender = new io.jaegertracing.Configuration.SenderConfiguration();
    // 將 <endpoint> 替換為控制台概覽頁面上相應用戶端和地區的存取點。
    sender.withEndpoint("<endpoint>");
    config.withSampler(new io.jaegertracing.Configuration.SamplerConfiguration().withType("const").withParam(1));
    config.withReporter(new io.jaegertracing.Configuration.ReporterConfiguration().withSender(sender).withMaxQueueSize(10000));
    GlobalTracer.register(config.getTracer());
  4. 記錄請求資料。

    Tracer tracer = GlobalTracer.get();
    // 建立Span。
    Span span = tracer.buildSpan("parentSpan").withTag("myTag", "spanFirst").start();
    tracer.scopeManager().activate(span, false);
    tracer.activeSpan().setTag("methodName", "testTracing");
    // 商務邏輯。
    secondBiz();
    span.finish();
  5. 可選:上一步用於記錄請求的根操作,如果需要記錄請求的上一步和下一步操作,則需要傳入上下文。

    Tracer tracer = GlobalTracer.get();
    Span parentspan = tracer.activeSpan();
    Tracer.SpanBuilder spanBuilder = tracer.buildSpan("childSpan").withTag("myTag", "spanSecond");
    if (parentspan !=null) {
        spanBuilder.asChildOf(parentspan).start();
    }
    Span childSpan = spanBuilder.start();
    Scope scope = tracer.scopeManager().activate(childSpan); // 請求開始執行一次
    // 商務邏輯。 可以執行多次 buildSpan
    childSpan.finish();
    tracer.activeSpan().setTag("methodName", "testCall");
    
    // 請求結束執行一次
    scope.close();
  6. 可選:為了方便排查問題,您可以為某個記錄添加一些自訂標籤(Tag),例如記錄是否發生錯誤、請求的傳回值等。

    tracer.activeSpan().setTag("methodName", "testCall");
  7. 在分布式系統中發送RPC請求時會帶上Tracing資料,包括TraceId、ParentSpanId、SpanId、Sampled等。您可以在HTTP請求中使用Extract/Inject方法在HTTP Request Headers上透傳資料。總體流程如下:

       Client Span                                                Server Span
    ┌──────────────────┐                                       ┌──────────────────┐
    │                  │                                       │                  │
    │   TraceContext   │           Http Request Headers        │   TraceContext   │
    │ ┌──────────────┐ │          ┌───────────────────┐        │ ┌──────────────┐ │
    │ │ TraceId      │ │          │ X-B3-TraceId      │        │ │ TraceId      │ │
    │ │              │ │          │                   │        │ │              │ │
    │ │ ParentSpanId │ │ Inject   │ X-B3-ParentSpanId │Extract │ │ ParentSpanId │ │
    │ │              ├─┼─────────>│                   ├────────┼>│              │ │
    │ │ SpanId       │ │          │ X-B3-SpanId       │        │ │ SpanId       │ │
    │ │              │ │          │                   │        │ │              │ │
    │ │ Sampled      │ │          │ X-B3-Sampled      │        │ │ Sampled      │ │
    │ └──────────────┘ │          └───────────────────┘        │ └──────────────┘ │
    │                  │                                       │                  │
    └──────────────────┘                                       └──────────────────┘
    1. 在用戶端調用Inject方法傳入Context資訊。

      private void attachTraceInfo(Tracer tracer, Span span, final Request request) {
              tracer.inject(span.context(), Format.Builtin.TEXT_MAP, new TextMap() {
                  @Override
                  public void put(String key, String value) {
                      request.setHeader(key, value);
                  }
                  @Override
                  public Iterator<Map.Entry<String, String>> iterator() {
                      throw new UnsupportedOperationException("TextMapInjectAdapter should only be used with Tracer.inject()");
                  }
              });
          }
    2. 在服務端調用Extract方法解析Context資訊。

      protected Span extractTraceInfo(Request request, Tracer tracer) {
          Tracer.SpanBuilder spanBuilder = tracer.buildSpan("/api/xtrace/test03");
          try {
              SpanContext spanContext = tracer.extract(Format.Builtin.TEXT_MAP, new TextMapExtractAdapter(request.getAttachments()));
              if (spanContext !=null) {
                  spanBuilder.asChildOf(spanContext);
              }
          } catch (Exception e) {
              spanBuilder.withTag("Error", "extract from request fail, error msg:" + e.getMessage());
          }
          return spanBuilder.start();
      }
                                      

通過Spring Cloud組件為Java應用埋點

要通過Jaeger將Java應用資料上報至Managed Service for OpenTelemetry控制台,首先需要完成埋點工作。本樣本為通過Spring Cloud組件埋點。Spring Cloud提供了下列組件的埋點:

  • @Async, @Scheduled, Executors

  • Feign, HystrixFeign

  • Hystrix

  • JDBC

  • JMS

  • Mongo

  • RabbitMQ

  • Redis

  • RxJava

  • Spring Messaging - 鏈路訊息通過訊息通道發送

  • Spring Web (RestControllers, RestTemplates, WebAsyncTask)

  • Standard Logging - 日誌被添加至當前Span

  • WebSocket STOMP

  • Zuul

請按照以下步驟通過Spring Cloud組件埋點。

說明

請下載Demo工程,進入springMvcDemo/webmvc4-boot目錄,並按照Readme的說明運行程式。

  1. 開啟pom.xml,添加Jar包依賴。

    <dependency>
       <groupId>io.opentracing.contrib</groupId>
      <artifactId>opentracing-spring-cloud-starter</artifactId>
      <version>0.5.8</version>
    </dependency>
    <dependency>
      <groupId>io.jaegertracing</groupId>
      <artifactId>jaeger-client</artifactId>
      <version>1.4.0</version>
    </dependency>
  2. 添加OpenTracing Tracer Bean。

    說明

    請將<endpoint>替換成可觀測鏈路 OpenTelemetry 版控制台叢集配置 > 存取點資訊頁面相應用戶端和地區的存取點。擷取存取點資訊的方法,請參見前提條件。

    @Bean
    public io.opentracing.Tracer tracer() {
        io.jaegertracing.Configuration config = new io.jaegertracing.Configuration("springFrontend");
        io.jaegertracing.Configuration.SenderConfiguration sender = new io.jaegertracing.Configuration.SenderConfiguration();
        sender.withEndpoint("<endpoint>");
        config.withSampler(new io.jaegertracing.Configuration.SamplerConfiguration().withType("const").withParam(1));
        config.withReporter(new io.jaegertracing.Configuration.ReporterConfiguration().withSender(sender).withMaxQueueSize(10000));
        return config.getTracer();
    }

通過gRPC組件為Java應用埋點

要通過Jaeger將Java應用資料上報至Managed Service for OpenTelemetry控制台,首先需要完成埋點工作。本樣本為通過gRPC組件埋點。

請按照以下步驟通過gRPC組件埋點。

說明

請下載Demo工程,進入grpcDemo目錄,並按照Readme的說明運行程式。

  1. 開啟pom.xml,添加Jar包依賴。

    <dependency>
        <groupId>io.opentracing.contrib</groupId>
        <artifactId>opentracing-grpc</artifactId>
        <version>0.2.3</version>
    </dependency>
  2. 在服務端初始化Tracing對象,建立ServerTracingInterceptor,並添加對Server的攔截。

    import io.opentracing.Tracer;
    
        public class YourServer {
    
            private int port;
            private Server server;
            private final Tracer tracer;
    
            private void start() throws IOException {
                ServerTracingInterceptor tracingInterceptor = new ServerTracingInterceptor(this.tracer);
    
                // If GlobalTracer is used: 
                // ServerTracingInterceptor tracingInterceptor = new ServerTracingInterceptor();
    
                server = ServerBuilder.forPort(port)
                    .addService(tracingInterceptor.intercept(someServiceDef))
                    .build()
                    .start();
            }
        }
  3. 在用戶端初始化Tracing對象,建立ClientTracingInterceptor,並添加對Client Channel的攔截。

    import io.opentracing.Tracer;
    
        public class YourClient {
    
            private final ManagedChannel channel;
            private final GreeterGrpc.GreeterBlockingStub blockingStub;
            private final Tracer tracer;
    
            public YourClient(String host, int port) {
    
                channel = ManagedChannelBuilder.forAddress(host, port)
                    .usePlaintext(true)
                    .build();
    
                ClientTracingInterceptor tracingInterceptor = new ClientTracingInterceptor(this.tracer);
    
                // If GlobalTracer is used: 
                // ClientTracingInterceptor tracingInterceptor = new ClientTracingInterceptor();
    
                blockingStub = GreeterGrpc.newBlockingStub(tracingInterceptor.intercept(channel));
            }
        }
                            

常見問題

問:Demo程式執行成功,為什麼控制台上沒有上報資料?

答:請調試方法io.jaegertracing.thrift.internal.senders.HttpSender.send(Process process, List<Span> spans),並查看上報資料時的傳回值。如果報403錯誤,則表明存取點配置不正確,請檢查並修改。