Trace the JAX-RS2 Java application created in Jersey with Zipkin.
Install docker.
Linux installs docker-compose additionally. Mac and Windows are not required because they are included in the Docker installer.
sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
Clone docker-zipkin from github and run Zipkin server with docker
git clone https://github.com/openzipkin/docker-zipkin.git
cd docker-zipkin
docker-compose up -d
You can access the Zipkin web UI at the following URL
The application is made with Jersey as the title suggests. I uploaded the completed one to github.
It utilizes Jersey 2, a server-side JAX-RS2 reference implementation.
Note that groupId progressivecom.sun.jersey``, which often appears when googled with Jersey, is not compatible with Jersey 1 series.
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-server -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.26</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.inject/jersey-hk2 -->
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.26</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.containers/jersey-container-servlet -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.26</version>
</dependency>
The client-side JAX-RS2 also makes use of the reference implementation Jersey 2.
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-client -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.26</version>
</dependency>
Web Application written in Java usually runs on Tomcat, Glassfish, Jetty, etc., but this time, for the sake of simplicity, use jersey-container-jdk-http
to implement the Http server. Embed directly in the application.
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.containers/jersey-container-jdk-http -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jdk-http</artifactId>
<version>2.26</version>
</dependency>
A Zipkin Instrumentation library for JAX-RS2 Java applications that allows you to trace on a JAX-RS server / client.
<!-- https://mvnrepository.com/artifact/io.zipkin.brave/brave-instrumentation-jaxrs2 -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-jaxrs2</artifactId>
<version>4.3.1</version>
</dependency>
The collected tracing data must be sent to the Zipkin server. This library will send it to any destination.
<!-- https://mvnrepository.com/artifact/io.zipkin.reporter/zipkin-sender-urlconnection -->
<dependency>
<groupId>io.zipkin.reporter</groupId>
<artifactId>zipkin-sender-urlconnection</artifactId>
<version>1.1.2</version>
</dependency>
JAX-RS2 allows you to plug in external libraries that implement the javax.ws.rs.core.Feature
IF.
Brave, a Zipkin library for Java applications, supports various Java frameworks. This time, we will use brave-instrumentation-jaxrs2 for JAX-RS2.
brave.jaxrs2.TracingFeature
is a Trace Instrumentation for JAX-RS2 that Creates Tracing Data (https://zipkin.io/pages/architecture.html). zipkin.reporter.AsyncReporter
sends the tracing data collected on the application to the Zipkin server.package zipkin;
import brave.Tracing;
import brave.jaxrs2.TracingFeature;
import brave.sampler.Sampler;
import zipkin.reporter.AsyncReporter;
import zipkin.reporter.urlconnection.URLConnectionSender;
import javax.ws.rs.core.Feature;
public class ZipkinFeature {
/**
* @param localServiceName Service name displayed in Zipkin Web UI
*/
public static Feature create(String localServiceName) {
// create a zipkin reporter.
AsyncReporter<Span> asyncReporter = AsyncReporter
.builder(URLConnectionSender.create("http://localhost:9411/api/v1/spans"))
.build();
// create a zipkin tracing.
Tracing tracing = Tracing.newBuilder()
.reporter(asyncReporter)
.localServiceName(localServiceName)
.sampler(Sampler.ALWAYS_SAMPLE)
.build();
// create a JAX-RS feature.
Feature tracingFeature = TracingFeature.create(tracing);
return tracingFeature;
}
}
Create a class that implements the server-side JAX-RS2 entry point javax.ws.rs.core.Application
IF. Here, the tracing function brave.jaxrs2.TracingFeature
generated earlier Register with server-side JAX-RS2.
package app;
import zipkin.ZipkinFeature;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Feature;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
@ApplicationPath("")
public class MyApplication extends Application {
@Override
public Set<Object> getSingletons() {
// create a JAX-RS feature.
Feature tracingFeature = ZipkinFeature.create("server");
return new LinkedHashSet<>(Arrays.asList(tracingFeature));
}
}
Create a resource class (API definition) for server-side JAX-RS2. Originally, I would like to deploy multiple services and check the operation of Zipkin, but for the sake of simplicity, define 3 APIs and client JAX. -Called in order by RS2.
Here, too, the tracing function brave.jaxrs2.TracingFeature
generated earlier is registered in the client JAX-RS2.
package resource;
import zipkin.ZipkinFeature;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("")
public class MyResource {
/**
* /front --> /mid --> /back
*
* @return String that will be returned as a text/plain response.
*/
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("front")
public Response front() {
// create a JAX-RS feature.
Feature tracingFeature = ZipkinFeature.create("client");
// http client: GET /mid
Response r = ClientBuilder.newClient()
.register(tracingFeature)
.target("http://localhost:8080/myapp/mid")
.request()
.get();
return Response.fromResponse(r).build();
}
/**
* /mid --> /back
*
* @return String that will be returned as a text/plain response.
*/
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("mid")
public Response mid() {
// create a JAX-RS feature.
Feature tracingFeature = ZipkinFeature.create("client");
// http client: GET /back
Response r = ClientBuilder.newClient()
.register(tracingFeature)
.target("http://localhost:8080/myapp/back")
.request()
.get();
return Response.fromResponse(r).build();
}
/**
* /back
*
* @return String that will be returned as a text/plain response.
*/
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("back")
public String back() {
return "hello";
}
}
We use jersey-container-jdk-http for the Http server implementation because we want it to work easily.
package main;
import app.MyApplication;
import com.sun.net.httpserver.HttpServer;
import org.glassfish.jersey.jdkhttp.JdkHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import java.net.URI;
public class Main {
public static void main(String[] args) {
// register JAX-RS Application class.
ResourceConfig rc = ResourceConfig.forApplicationClass(MyApplication.class);
// register a path of REST resources.
rc.packages(true, "resource");
// run a jersey server.
URI uri = URI.create("http://localhost:8080/myapp/");
HttpServer httpServer = JdkHttpServerFactory.createHttpServer(uri, rc);
Runtime.getRuntime().addShutdownHook(new Thread(() -> httpServer.stop(0)));
}
}
Run the application. You can access it at the following URL.
Visit the Zipkin Web UI. You should be able to see the tracing data you just accessed.