全部產品
Search
文件中心

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

更新時間:Aug 07, 2024

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

重要

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

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

前提條件

擷取存取點資訊

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

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

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

    說明

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

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

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

    image.png

背景資訊

Zipkin是一款開源的分布式即時資料追蹤系統(Distributed Tracking System),由Twitter公司開發和貢獻。其主要功能是彙總來自各個異構系統的即時監控資料。

Zipkin已經開發多年,對各種架構的支援比較齊全,例如以下架構,完整資訊請參考Zipkin官方文檔

  • Apache HttpClient

  • Dubbo

  • gRPC

  • JAX-RS 2.X

  • Jersey Server

  • JMS (Java Message Service)

  • Kafka

  • MySQL

  • Netty

  • OkHttp

  • Servlet

  • Spark

  • Spring Boot

  • Spring MVC

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

通過Zipkin上報資料的原理

手動埋點

如果選擇手動埋點,您就需要自行編寫代碼。

說明

如需擷取Demo,請單擊下載源碼,進入manualDemo目錄,並根據Readme運行程式。

  1. 添加依賴Jar包。

    <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave</artifactId>
                <version>5.4.2</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.reporter2</groupId>
                <artifactId>zipkin-sender-okhttp3</artifactId>
                <version>2.7.9</version>
            </dependency>
  2. 建立Tracer。

    private static final String zipkinEndPoint = "<endpoint>";
      ...
      // 構建資料發送對象。
      OkHttpSender sender = OkHttpSender.newBuilder().endpoint(zipkinEndPoint).build();
    
      // 構建資料上報對象。
      Reporter<Span> reporter = AsyncReporter.builder(sender).build();
    
      tracing = Tracing.newBuilder().localServiceName(localServiceName).spanReporter(reporter).build();
  3. 構建Span和Child Span。

    private void firstBiz() {
            // 建立rootspan。
            tracing.tracer().startScopedSpan("parentSpan");
            Span span =  tracing.tracer().currentSpan();
            span.tag("key", "firstBiz");
            secondBiz();
            span.finish();
        }
    
        private void secondBiz() {
            tracing.tracer().startScopedSpanWithParent("childSpan", tracing.tracer().currentSpan().context());
            Span childSpan =  tracing.tracer().currentSpan();
            childSpan.tag("key", "secondBiz");
            childSpan.finish();
            System.out.println("end tracing,id:" + childSpan.context().traceIdString());
        }
  4. (可選)為了快速排查問題,您可以為某個記錄添加一些自訂標籤,例如記錄是否發生錯誤、請求的傳回值等。

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

    流程圖

    1. 在用戶端調用Inject方法傳入Context資訊。

      // start a new span representing a client request
          oneWaySend = tracer.nextSpan().name(service + "/" + method).kind(CLIENT);
          --snip--
      
          // Add the trace context to the request, so it can be propagated in-band
          tracing.propagation().injector(Request::addHeader)
                           .inject(oneWaySend.context(), request);
      
         // fire off the request asynchronously, totally dropping any response
         request.execute();
      
         // start the client side and flush instead of finish
         oneWaySend.start().flush();
    2. 在服務端調用Extract方法解析Context資訊。

      // pull the context out of the incoming request
      extractor = tracing.propagation().extractor(Request::getHeader);
      
      // convert that context to a span which you can name and add tags to
      oneWayReceive = nextSpan(tracer, extractor.extract(request))
          .name("process-request")
          .kind(SERVER)
          ... add tags etc.
      
      // start the server side and flush instead of finish
      oneWayReceive.start().flush();
      
      // you should not modify this span anymore as it is complete. However,
      // you can create children to represent follow-up work.
      next = tracer.newSpan(oneWayReceive.context()).name("step2").start();

通過Spring 2.5 MVC或Spring 3.0 MVC外掛程式埋點

您可以選擇通過Spring 2.5 MVC或Spring 3.0 MVC外掛程式進行埋點。

說明

如需擷取Demo,請單擊下載源碼,進入springMvcDemo\webmvc3|webmvc25目錄,並根據Readme運行程式。

  1. applicationContext.xml中配置Tracing對象。

    <bean class="zipkin2.reporter.beans.OkHttpSenderFactoryBean">
      <property name="endpoint" value="<endpoint>"/>
    </bean>
    
    <!-- allows us to read the service name from spring config -->
    <context:property-placeholder/>
    
    <bean class="brave.spring.beans.TracingFactoryBean">
      <property name="localServiceName" value="brave-webmvc3-example"/>
      <property name="spanReporter">
        <bean class="zipkin2.reporter.beans.AsyncReporterFactoryBean">
          <property name="encoder" value="JSON_V2"/>
          <property name="sender" ref="sender"/>
          <!-- wait up to half a second for any in-flight spans on close -->
          <property name="closeTimeout" value="500"/>
        </bean>
      </property>
      <property name="propagationFactory">
        <bean class="brave.propagation.ExtraFieldPropagation" factory-method="newFactory">
          <constructor-arg index="0">
            <util:constant static-field="brave.propagation.B3Propagation.FACTORY"/>
          </constructor-arg>
          <constructor-arg index="1">
            <list>
              <value>user-name</value>
            </list>
          </constructor-arg>
        </bean>
      </property>
      <property name="currentTraceContext">
        <bean class="brave.spring.beans.CurrentTraceContextFactoryBean">
          <property name="scopeDecorators">
            <bean class="brave.context.log4j12.MDCScopeDecorator" factory-method="create"/>
          </property>
        </bean>
      </property>
    </bean>
    
    <bean class="brave.spring.beans.HttpTracingFactoryBean">
      <property name="tracing" ref="tracing"/>
    </bean>
  2. 添加Interceptors對象。

    <bean class="brave.httpclient.TracingHttpClientBuilder"
          factory-method="create">
        <constructor-arg type="brave.http.HttpTracing" ref="httpTracing"/>
      </bean>
    
      <bean factory-bean="httpClientBuilder" factory-method="build"/>
    
      <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
        <property name="interceptors">
          <list>
            <bean class="brave.spring.webmvc.SpanCustomizingHandlerInterceptor"/>
          </list>
        </property>
      </bean>
    
      <!-- Loads the controller -->
      <context:component-scan base-package="brave.webmvc"/>
  3. 添加Filter對象。

    <!-- Add the delegate to the standard tracing filter and map it to all paths -->
    <filter>
      <filter-name>tracingFilter</filter-name>
      <filter-class>brave.spring.webmvc.DelegatingTracingFilter</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>tracingFilter</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>

通過Spring 4.0 MVC或Spring Boot外掛程式埋點

您可以選擇通過Spring 4.0 MVC或Spring Boot外掛程式進行埋點。

說明

如需擷取Demo,請單擊下載源碼,進入springMvcDemo\webmvc4-boot|webmv4目錄,並根據Readme運行程式。

  1. 配置Tracing和Filter。

    /** Configuration for how to send spans to Zipkin */
      @Bean Sender sender() {
        return OkHttpSender.create("<endpoint>");
      }
    
      /** Configuration for how to buffer spans into messages for Zipkin */
      @Bean AsyncReporter<Span> spanReporter() {
        return AsyncReporter.create(sender());
      }
    
      /** Controls aspects of tracing such as the name that shows up in the UI */
      @Bean Tracing tracing(@Value("${spring.application.name}") String serviceName) {
        return Tracing.newBuilder()
            .localServiceName(serviceName)
            .propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "user-name"))
            .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()
                .addScopeDecorator(MDCScopeDecorator.create()) // puts trace IDs into logs
                .build()
            )
            .spanReporter(spanReporter()).build();
      }
    
      /** decides how to name and tag spans. By default they are named the same as the http method. */
      @Bean HttpTracing httpTracing(Tracing tracing) {
        return HttpTracing.create(tracing);
      }
    
      /** Creates client spans for http requests */
      // We are using a BPP as the Frontend supplies a RestTemplate bean prior to this configuration
      @Bean BeanPostProcessor connectionFactoryDecorator(final BeanFactory beanFactory) {
        return new BeanPostProcessor() {
          @Override public Object postProcessBeforeInitialization(Object bean, String beanName) {
            return bean;
          }
    
          @Override public Object postProcessAfterInitialization(Object bean, String beanName) {
            if (!(bean instanceof RestTemplate)) return bean;
    
            RestTemplate restTemplate = (RestTemplate) bean;
            List<ClientHttpRequestInterceptor> interceptors =
                new ArrayList<>(restTemplate.getInterceptors());
            interceptors.add(0, getTracingInterceptor());
            restTemplate.setInterceptors(interceptors);
            return bean;
          }
    
          // Lazy lookup so that the BPP doesn't end up needing to proxy anything.
          ClientHttpRequestInterceptor getTracingInterceptor() {
            return TracingClientHttpRequestInterceptor.create(beanFactory.getBean(HttpTracing.class));
          }
        };
      }
    
      /** Creates server spans for http requests */
      @Bean Filter tracingFilter(HttpTracing httpTracing) {
        return TracingFilter.create(httpTracing);
      }
    
      @Autowired SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;
    
      /** Decorates server spans with application-defined web tags */
      @Override public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(webMvcTracingCustomizer);
      }
  2. 配置autoconfigure(spring.factories)。

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    brave.webmvc.TracingConfiguration

通過Dubbo外掛程式埋點

您可以選擇通過Dubbo外掛程式進行埋點。

說明

如需擷取Demo,請單擊下載源碼,進入dubboDemo目錄,並根據Readme運行程式。

  1. 添加依賴Jar包。

    <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave-instrumentation-dubbo-rpc</artifactId>
                <version>5.4.2</version>
            </dependency>
    
            <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave-spring-beans</artifactId>
                <version>5.4.2</version>
            </dependency>
    
            <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave-context-slf4j</artifactId>
                <version>5.4.2</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.reporter2</groupId>
                <artifactId>zipkin-sender-okhttp3</artifactId>
                <version>2.7.9</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave</artifactId>
                <version>5.4.2</version>
            </dependency>
    
            <dependency>
                <groupId>io.zipkin.reporter2</groupId>
                <artifactId>zipkin-sender-okhttp3</artifactId>
                <version>2.7.9</version>
            </dependency>
  2. 配置Tracing對象。

    <bean class="zipkin2.reporter.beans.OkHttpSenderFactoryBean">
            <property name="endpoint" value="<endpoint>"/>
        </bean>
    
        <bean class="brave.spring.beans.TracingFactoryBean">
            <property name="localServiceName" value="double-provider"/>
            <property name="spanReporter">
                <bean class="zipkin2.reporter.beans.AsyncReporterFactoryBean">
                    <property name="sender" ref="sender"/>
                    <!-- wait up to half a second for any in-flight spans on close -->
                    <property name="closeTimeout" value="500"/>
                </bean>
            </property>
            <property name="currentTraceContext">
                <bean class="brave.spring.beans.CurrentTraceContextFactoryBean">
                    <property name="scopeDecorators">
                        <bean class="brave.context.slf4j.MDCScopeDecorator" factory-method="create"/>
                    </property>
                </bean>
            </property>
        </bean>
  3. 添加Filter配置。

    // 服務端配置。
    <dubbo:provider filter="tracing" />
    // 用戶端配置。
    <dubbo:consumer filter="tracing" />

通過Spring Sleuth外掛程式埋點

您可以選擇通過Spring Sleuth外掛程式進行埋點。

說明

如需擷取Demo,請單擊下載源碼,進入sleuthDemo目錄,並根據Readme運行程式。

  1. 添加依賴Jar包。

    <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave</artifactId>
                <version>5.4.2</version>
            </dependency>
    
            <dependency>
                <groupId>io.zipkin.reporter2</groupId>
                <artifactId>zipkin-sender-okhttp3</artifactId>
                <version>2.7.9</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>2.0.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-sleuth-core</artifactId>
                <version>2.0.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-sleuth-zipkin</artifactId>
                <version>2.0.1.RELEASE</version>
            </dependency>
  2. 配置application.yml。

    說明

    請將<endpoint_short>替換成前提條件中擷取的存取點資訊(“公網存取點:”後面到“api/v2/spans”之前的內容)。

    spring:
       application:
         # This ends up as the service name in zipkin
         name: sleuthDemo
       zipkin:
         # Uncomment to send to zipkin, replacing 192.168.99.100 with your zipkin IP address
         baseUrl: <endpoint_short>
    
       sleuth:
         sampler:
           probability: 1.0
    
       sample:
       zipkin:
         # When enabled=false, traces log to the console. Comment to send to zipkin
         enabled: true
  3. 發起HTTP請求,例如http://localhost:3380/traced

    說明

    更多請求路徑,請參見Demo中com.alibaba.apm.SampleController下的方法。

常見問題

問:Demo程式執行成功,但是為什麼有的網站上無資料?

答:請斷點調試zipkin2.reporter.okhttp3.HttpCall中的parseResponse方法,查看上報資料時傳回值。如果報403錯誤,表示使用者名稱配置不正確,需要檢查Endpoint配置。