Spring Cloud Sleuth Zipkin example
When using a microservices architecture, a single transaction may span multiple microservices. If you want to debug any transaction
or you want to investigate an issue, you need a unique id to identify the flow of transactions.
Spring Cloud Sleuth
provides the solution by implementing distributed tracing solution for Spring Cloud.
Spring Cloud Sleuth
uses correlation IDs (traceid)
to trace logs. A correlation ID
is a unique number or string (e.g. order id, user id) that's assigned to a transaction when a transaction is initiated. As the transaction flows across multiple services, the correlation ID
is propagated from one service call to another. With Spring Cloud Sleuth
,
you'll automatically get correlation IDs
added to the log statements you put in your microservices.
We will also configure Zipkin
in these microservices so that API call statistics will be send to Zipkin
server.
Zipkin
is a distributed tracing platform that will allow us to trace transactions across multiple service invocations.
Using Zipkin
we can graphically check the time consumed by each microservice API.
Step 1) Add spring-cloud-starter-sleuth
dependency to pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath /> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>2.1.3.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> <version>${spring-cloud.version}</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin</artifactId> <version>${spring-cloud.version}</version> </dependency> </dependencies>
Step 2) Create first microservice
Add SleuthApplication, SleuthRestController classes and application.yml file. SleuthRestController will expose an endpoint
that will we will call from browser and it will internally call second microservice to get the results. By default Zipkin
server
runs at port 9411. It it is running on some other port or host, we can configure the zipkin url using spring.zipkin.baseUrl
property in application.yml
file.
We will need to define how often each service should write data to Zipkin
. By default only 10% data is sent to Zipkin
.
However for our testing purpose we can make it 100% by using spring.sleuth.sampler.percentage
property which should be set between
0 & 1. It is not recommended to set it to 1 as it will put hugh load on Zipkin
server.
package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class SleuthApplication { public static void main(String[] args) { SpringApplication.run(SleuthApplication.class, args); } @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
package com.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class SleuthRestController { private final Logger LOG = LoggerFactory.getLogger(this.getClass()); @Autowired private RestTemplate restTemplate; @RequestMapping(value = "/api") public String hello() { LOG.info("Hello Sleuth"); return restTemplate.getForObject("http://localhost:8082/api2", String.class); } }
spring: application: name: sleuth-demo zipkin: baseUrl: http://localhost:9411 sleuth: sampler: probability: 1.0 server: port: 8081
Step 3) Create second microservice
Add SleuthApplication2, SleuthRestController classes and application.properties file. SleuthRestController will expose an endpoint that will be called by first microservice.
package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SleuthApplication2 { public static void main(String[] args) { SpringApplication.run(SleuthApplication2.class, args); } }
package com.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class SleuthRestController { private final Logger LOG = LoggerFactory.getLogger(this.getClass()); @RequestMapping(value = "/api2") public String hello() { LOG.info("Hello Sleuth you have invoked second microservice"); return "Hello Sleuth you have invoked second microservice"; } }
spring: application: name: sleuth-demo-2 zipkin: baseUrl: http://localhost:9411 sleuth: sampler: probability: 1.0 server: port: 8082
Step 4) Configure Zipkin server
Zipkin
server in 2 ways. One is by downloading executable jar from
latest release
and then start the server using java -jar zipkin.jar
command.
Other way is to configure using Spring Boot by adding below dependency and then starting application with @EnableZipkinServer
.
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Edgware.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-server</artifactId> </dependency> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-ui</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import zipkin.server.EnableZipkinServer; @EnableZipkinServer @SpringBootApplication public class ZipkinServiceApplication { public static void main(String[] args) { SpringApplication.run(ZipkinServiceApplication.class, args); } }
Step 4) Running the microservices and Zipkin server
Hello Sleuth you have invoked second microservice
Console Output :
Observe the logs in SleuthApplication, it prints sleuth-demo,61a0a37a9c5988af,61a0a37a9c5988af,true
- sleuth-demo is the name of the service being logged.
- 61a0a37a9c5988af is the Trace ID which is a unique identifier for the user's request that will be carried across all service calls in that request.
- Next 61a0a37a9c5988af is the Span ID which is a unique identifier for a single segment in the overall user request. For multi-service calls, there will be one span ID for each service call in the user transaction.
- true is the flag indicating whether the data will be sent to the Zipkin server for tracing.
Observe the logs in SleuthApplication2, it prints sleuth-demo-2,61a0a37a9c5988af,f7d587d622ac96cc,true
where f7d587d622ac96cc
is the span id.
Step 5) Check microservices stats in Zipkin UI
If you click on '2 spans' you will see how much time is taken by each microservice. You can also search for a specific transaction
by providing trace id like 61a0a37a9c5988af
References :
Spring Cloud Sleuth
OpenZipkin