通過Jaeger為應用埋點並上報鏈路資料至Managed Service for OpenTelemetry後,Managed Service for OpenTelemetry即可開始監控應用,您可以查看應用拓撲、調用鏈路、異常事務、慢事務和SQL分析等一系列監控資料。本文介紹如何進行手動埋點,以及使用Spring Cloud組件和gRPC組件進行自動埋點。
為獲得更豐富的功能、更先進的鏈路追蹤能力,以及最佳使用體驗,建議您使用OpenTelemetry協議將應用接入Managed Service for OpenTelemetry。
我們為您提供了詳細的OpenTelemetry接入指南和最佳實務,協助您快速上手Managed Service for OpenTelemetry。更多資訊,請參見接入應用。
前提條件
背景資訊
Jaeger是一款開源分布式追蹤系統,相容OpenTracing API,且已加入CNCF開源組織。其主要功能是彙總來自各個異構系統的即時監控資料。目前OpenTracing社區已有許多組件可支援各種Java架構,例如:
要通過Jaeger將Java應用資料上報至Managed Service for OpenTelemetry控制台,首先需要完成埋點工作。您可以手動埋點,也可以利用各種現有外掛程式實現埋點的目的。本文介紹以下三種埋點方法。
手動埋點
通過Spring Cloud組件埋點
通過gRPC組件埋點
為Java應用手動埋點
要通過Jaeger將Java應用資料上報至可觀測鏈路 OpenTelemetry 版控制台,首先需要完成埋點工作。本樣本為手動埋點。
下載Demo工程,進入manualDemo目錄,並按照Readme的說明運行程式。
開啟pom.xml,添加對Jaeger用戶端的依賴。
<dependency> <groupId>io.jaegertracing</groupId> <artifactId>jaeger-client</artifactId> <version>0.31.0</version> </dependency>
配置初始化參數並建立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());
記錄請求資料。
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();
可選:上一步用於記錄請求的根操作,如果需要記錄請求的上一步和下一步操作,則需要傳入上下文。
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();
可選:為了方便排查問題,您可以為某個記錄添加一些自訂標籤(Tag),例如記錄是否發生錯誤、請求的傳回值等。
tracer.activeSpan().setTag("methodName", "testCall");
在分布式系統中發送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 │ │ │ └──────────────┘ │ └───────────────────┘ │ └──────────────┘ │ │ │ │ │ └──────────────────┘ └──────────────────┘
在用戶端調用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()"); } }); }
在服務端調用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的說明運行程式。
開啟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>
添加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的說明運行程式。
開啟pom.xml,添加Jar包依賴。
<dependency> <groupId>io.opentracing.contrib</groupId> <artifactId>opentracing-grpc</artifactId> <version>0.2.3</version> </dependency>
在服務端初始化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(); } }
在用戶端初始化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錯誤,則表明存取點配置不正確,請檢查並修改。