Refactor tracing demo to show A->B->C call stack. (#436)

This commit is contained in:
Artur Souza 2021-01-08 15:03:00 -08:00 committed by GitHub
parent 6a44d51279
commit 9e13586ecc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 167 additions and 51 deletions

View File

@ -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));

View File

@ -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));

View File

@ -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();
}
}

View File

@ -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 {

View File

@ -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());

View File

@ -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();
}
}

View File

@ -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