mirror of https://github.com/dapr/java-sdk.git
Refactor tracing demo to show A->B->C call stack. (#436)
This commit is contained in:
parent
6a44d51279
commit
9e13586ecc
|
@ -31,7 +31,7 @@ public class InvokeClient {
|
|||
/**
|
||||
* Identifier in Dapr for the service this client will invoke.
|
||||
*/
|
||||
private static final String SERVICE_APP_ID = "tracingdemo";
|
||||
private static final String SERVICE_APP_ID = "tracingdemoproxy";
|
||||
|
||||
/**
|
||||
* Starts the invoke client.
|
||||
|
@ -45,7 +45,7 @@ public class InvokeClient {
|
|||
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
||||
for (String message : args) {
|
||||
try (Scope scope = span.makeCurrent()) {
|
||||
InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "echo");
|
||||
InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "proxy_echo");
|
||||
InvokeServiceRequest request
|
||||
= builder.withBody(message).withHttpExtension(HttpExtension.POST).withContext(Context.current()).build();
|
||||
client.invokeMethod(request, TypeRef.get(byte[].class))
|
||||
|
@ -54,7 +54,7 @@ public class InvokeClient {
|
|||
return r;
|
||||
})
|
||||
.flatMap(r -> {
|
||||
InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "sleep")
|
||||
InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "proxy_sleep")
|
||||
.withHttpExtension(HttpExtension.POST)
|
||||
.withContext(r.getContext()).build();
|
||||
return client.invokeMethod(sleepRequest, TypeRef.get(Void.class));
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
In this sample, we'll create two java applications: a service application, which exposes two methods, and a client application which will invoke the methods from the service using Dapr.
|
||||
This sample includes:
|
||||
|
||||
* TracingDemoService (Exposes the method to be remotely accessed)
|
||||
* TracingDemoService (Exposes the methods to be remotely accessed)
|
||||
* TracingDemoServiceController (Implements two methods: `echo` and `sleep`)
|
||||
* TracingDemoMiddleServiceController (Implements two methods: `proxy_echo` and `proxy_sleep`)
|
||||
* InvokeClient (Invokes the exposed methods from TracingDemoService)
|
||||
|
||||
Also consider [getting started with observability in Dapr](https://github.com/dapr/quickstarts/tree/master/observability).
|
||||
|
@ -54,7 +56,7 @@ CONTAINER ID IMAGE COMMAND CREATED
|
|||
|
||||
If Zipkin is not working, [install the newest version of Dapr Cli and initialize it](https://github.com/dapr/cli#install-dapr-on-your-local-machine-self-hosted).
|
||||
|
||||
### Running the Demo service sample
|
||||
### Running the Demo service app
|
||||
|
||||
The Demo service application exposes two methods that can be remotely invoked. In this example, the service code has two parts:
|
||||
|
||||
|
@ -82,11 +84,11 @@ This Rest Controller exposes the `echo` and `sleep` methods. The `echo` method r
|
|||
public class TracingDemoServiceController {
|
||||
///...
|
||||
@PostMapping(path = "/echo")
|
||||
public Mono<String> handleMethod(@RequestBody(required = false) byte[] body,
|
||||
public Mono<String> handleMethod(@RequestBody(required = false) String body,
|
||||
@RequestHeader Map<String, String> headers) {
|
||||
return Mono.fromSupplier(() -> {
|
||||
try {
|
||||
String message = body == null ? "" : new String(body, StandardCharsets.UTF_8);
|
||||
String message = body == null ? "" : body;
|
||||
|
||||
Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
String utcNowAsString = DATE_FORMAT.format(utcNow.getTime());
|
||||
|
@ -95,7 +97,7 @@ public class TracingDemoServiceController {
|
|||
|
||||
// Handles the request by printing message.
|
||||
System.out.println(
|
||||
"Server: " + message + " @ " + utcNowAsString + " and metadata: " + metadataString);
|
||||
"Server: " + message + " @ " + utcNowAsString + " and metadata: " + metadataString);
|
||||
|
||||
return utcNowAsString;
|
||||
} catch (Exception e) {
|
||||
|
@ -130,15 +132,67 @@ dapr run --app-id tracingdemo --app-port 3000 -- java -jar target/dapr-java-sdk-
|
|||
|
||||
Once running, the TracingDemoService is now ready to be invoked by Dapr.
|
||||
|
||||
### Running the Demo middle service app
|
||||
|
||||
### Running the InvokeClient sample
|
||||
This service will handle the `proxy_echo` and `proxy_sleep` methods and invoke the corresponding methods in the service above.
|
||||
In the code below, the `opentelemetry-context` attribute is used to propagate the tracing context among service invocations in multiple layers.
|
||||
|
||||
This sample code uses the Dapr SDK for invoking two remote methods (`echo` and `sleep`). It is also instrumented with OpenTelemetry. See the code snippet below:
|
||||
```java
|
||||
@RestController
|
||||
public class TracingDemoMiddleServiceController {
|
||||
// ...
|
||||
@PostMapping(path = "/proxy_echo")
|
||||
public Mono<byte[]> echo(
|
||||
@RequestAttribute(name = "opentelemetry-context") Context context,
|
||||
@RequestBody(required = false) String body) {
|
||||
InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(INVOKE_APP_ID, "echo");
|
||||
InvokeServiceRequest request
|
||||
= builder.withBody(body).withHttpExtension(HttpExtension.POST).withContext(context).build();
|
||||
return client.invokeMethod(request, TypeRef.get(byte[].class)).map(r -> r.getObject());
|
||||
}
|
||||
// ...
|
||||
@PostMapping(path = "/proxy_sleep")
|
||||
public Mono<Void> sleep(@RequestAttribute(name = "opentelemetry-context") Context context) {
|
||||
InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(INVOKE_APP_ID, "sleep");
|
||||
InvokeServiceRequest request = builder.withHttpExtension(HttpExtension.POST).withContext(context).build();
|
||||
return client.invokeMethod(request, TypeRef.get(byte[].class)).then();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The request attribute `opentelemetry-context` in created by parsing the tracing headers in the [OpenTelemetryInterceptor](../../springboot/OpenTelemetryInterceptor.java) class. See the code below:
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class OpenTelemetryInterceptor implements HandlerInterceptor {
|
||||
// ...
|
||||
@Override
|
||||
public boolean preHandle(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
final TextMapPropagator textFormat = OpenTelemetry.getGlobalPropagators().getTextMapPropagator();
|
||||
// ...
|
||||
Context context = textFormat.extract(Context.current(), request, HTTP_SERVLET_REQUEST_GETTER);
|
||||
request.setAttribute("opentelemetry-context", context);
|
||||
return true;
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Use the follow command to execute the service:
|
||||
|
||||
```sh
|
||||
dapr run --app-id tracingdemoproxy --app-port 3001 -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.tracing.TracingDemoService -p 3001
|
||||
```
|
||||
|
||||
### Running the InvokeClient app
|
||||
|
||||
This sample code uses the Dapr SDK for invoking two remote methods (`proxy_echo` and `proxy_sleep`). It is also instrumented with OpenTelemetry. See the code snippet below:
|
||||
|
||||
```java
|
||||
public class InvokeClient {
|
||||
|
||||
private static final String SERVICE_APP_ID = "invokedemo";
|
||||
private static final String SERVICE_APP_ID = "tracingdemoproxy";
|
||||
///...
|
||||
public static void main(String[] args) throws Exception {
|
||||
Tracer tracer = OpenTelemetryConfig.createTracer(InvokeClient.class.getCanonicalName());
|
||||
|
@ -147,7 +201,7 @@ private static final String SERVICE_APP_ID = "invokedemo";
|
|||
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
||||
for (String message : args) {
|
||||
try (Scope scope = tracer.withSpan(span)) {
|
||||
InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "echo");
|
||||
InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "proxy_echo");
|
||||
InvokeServiceRequest request
|
||||
= builder.withBody(message).withHttpExtension(HttpExtension.POST).withContext(Context.current()).build();
|
||||
client.invokeService(request, TypeRef.get(byte[].class))
|
||||
|
@ -156,7 +210,7 @@ private static final String SERVICE_APP_ID = "invokedemo";
|
|||
return r;
|
||||
})
|
||||
.flatMap(r -> {
|
||||
InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "sleep")
|
||||
InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "proxy_sleep")
|
||||
.withHttpExtension(HttpExtension.POST)
|
||||
.withContext(r.getContext()).build();
|
||||
return client.invokeMethod(sleepRequest, TypeRef.get(Void.class));
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.tracing;
|
||||
|
||||
import io.dapr.client.DaprClient;
|
||||
import io.dapr.client.domain.HttpExtension;
|
||||
import io.dapr.client.domain.InvokeServiceRequest;
|
||||
import io.dapr.client.domain.InvokeServiceRequestBuilder;
|
||||
import io.dapr.utils.TypeRef;
|
||||
import io.opentelemetry.context.Context;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* SpringBoot Controller to handle service invocation.
|
||||
*
|
||||
* <p>Instrumentation is handled in {@link io.dapr.springboot.OpenTelemetryInterceptor}.
|
||||
*/
|
||||
@RestController
|
||||
public class TracingDemoMiddleServiceController {
|
||||
|
||||
private static final String INVOKE_APP_ID = "tracingdemo";
|
||||
|
||||
/**
|
||||
* Dapr client.
|
||||
*/
|
||||
@Autowired
|
||||
private DaprClient client;
|
||||
|
||||
/**
|
||||
* Handles the 'echo' method invocation, by proxying a call into another service.
|
||||
*
|
||||
* @param context The tracing context for the request.
|
||||
* @param body The body of the http message.
|
||||
* @return A message containing the time.
|
||||
*/
|
||||
@PostMapping(path = "/proxy_echo")
|
||||
public Mono<byte[]> echo(
|
||||
@RequestAttribute(name = "opentelemetry-context") Context context,
|
||||
@RequestBody(required = false) String body) {
|
||||
InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(INVOKE_APP_ID, "echo");
|
||||
InvokeServiceRequest request
|
||||
= builder.withBody(body).withHttpExtension(HttpExtension.POST).withContext(context).build();
|
||||
return client.invokeMethod(request, TypeRef.get(byte[].class)).map(r -> r.getObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the 'sleep' method invocation, by proxying a call into another service.
|
||||
*
|
||||
* @param context The tracing context for the request.
|
||||
* @return A message containing the time.
|
||||
*/
|
||||
@PostMapping(path = "/proxy_sleep")
|
||||
public Mono<Void> sleep(@RequestAttribute(name = "opentelemetry-context") Context context) {
|
||||
InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(INVOKE_APP_ID, "sleep");
|
||||
InvokeServiceRequest request = builder.withHttpExtension(HttpExtension.POST).withContext(context).build();
|
||||
return client.invokeMethod(request, TypeRef.get(byte[].class)).then();
|
||||
}
|
||||
|
||||
}
|
|
@ -22,6 +22,9 @@ import org.apache.commons.cli.Options;
|
|||
* 3. Run in server mode:
|
||||
* dapr run --app-id tracingdemo --app-port 3000 \
|
||||
* -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.tracing.TracingDemoService -p 3000
|
||||
* 4. Run middle server:
|
||||
* dapr run --app-id tracingdemoproxy --app-port 3001 \
|
||||
* -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.tracing.TracingDemoService -p 3001
|
||||
*/
|
||||
public class TracingDemoService {
|
||||
|
||||
|
|
|
@ -53,11 +53,11 @@ public class TracingDemoServiceController {
|
|||
* @return A message containing the time.
|
||||
*/
|
||||
@PostMapping(path = "/echo")
|
||||
public Mono<String> handleMethod(@RequestBody(required = false) byte[] body,
|
||||
public Mono<String> handleMethod(@RequestBody(required = false) String body,
|
||||
@RequestHeader Map<String, String> headers) {
|
||||
return Mono.fromSupplier(() -> {
|
||||
try {
|
||||
String message = body == null ? "" : new String(body, StandardCharsets.UTF_8);
|
||||
String message = body == null ? "" : body;
|
||||
|
||||
Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
String utcNowAsString = DATE_FORMAT.format(utcNow.getTime());
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
package io.dapr.springboot;
|
||||
|
||||
import io.dapr.client.DaprClient;
|
||||
import io.dapr.client.DaprClientBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class DaprConfig {
|
||||
|
||||
private static final DaprClientBuilder BUILDER = new DaprClientBuilder();
|
||||
|
||||
@Bean
|
||||
public DaprClient buildDaprClient() {
|
||||
return BUILDER.build();
|
||||
}
|
||||
}
|
|
@ -6,12 +6,9 @@
|
|||
package io.dapr.springboot;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.api.trace.Tracer;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
@ -36,8 +33,6 @@ public class OpenTelemetryInterceptor implements HandlerInterceptor {
|
|||
return carrier.getHeader(key);
|
||||
}
|
||||
};
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(
|
||||
|
@ -49,43 +44,15 @@ public class OpenTelemetryInterceptor implements HandlerInterceptor {
|
|||
return true;
|
||||
}
|
||||
|
||||
Span span;
|
||||
try {
|
||||
Context context = textFormat.extract(Context.current(), request, HTTP_SERVLET_REQUEST_GETTER);
|
||||
request.setAttribute("opentelemetry-context", context);
|
||||
span = tracer.spanBuilder(request.getRequestURI()).setParent(context).startSpan();
|
||||
span.setAttribute("handler", "pre");
|
||||
} catch (Exception e) {
|
||||
span = tracer.spanBuilder(request.getRequestURI()).startSpan();
|
||||
span.setAttribute("handler", "pre");
|
||||
|
||||
span.addEvent(e.toString());
|
||||
span.setAttribute("error", true);
|
||||
}
|
||||
request.setAttribute("opentelemetry-span", span);
|
||||
Context context = textFormat.extract(Context.current(), request, HTTP_SERVLET_REQUEST_GETTER);
|
||||
request.setAttribute("opentelemetry-context", context);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postHandle(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler,
|
||||
ModelAndView modelAndView) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception exception) {
|
||||
Object contextObject = request.getAttribute("opentelemetry-context");
|
||||
Object spanObject = request.getAttribute("opentelemetry-span");
|
||||
if ((contextObject == null) || (spanObject == null)) {
|
||||
return;
|
||||
}
|
||||
Context context = (Context) contextObject;
|
||||
Span span = (Span) spanObject;
|
||||
span.setAttribute("handler", "afterCompletion");
|
||||
final TextMapPropagator textFormat = OpenTelemetry.getGlobalPropagators().getTextMapPropagator();
|
||||
textFormat.inject(context, response, HttpServletResponse::addHeader);
|
||||
span.end();
|
||||
ModelAndView modelAndView) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 735 KiB After Width: | Height: | Size: 336 KiB |
Loading…
Reference in New Issue