
Spring Cloud + Sleuth
2021, Nov 20
Hướng dẫn tích hợp Spring Cloud Sleuth trong ứng dụng Spring Boot
Example

- Trong ví dụ này bao gồm 3 service
- ServiceA: một service public 2 api và gọi vào ServiceB, ServiceC
- ServiceB: http service
- ServiceC: grpc service
- Thêm dependency spring-cloud-starter-sleuth trong mỗi service
<properties>
<spring-cloud.version>2020.0.4</spring-cloud.version>
</properties>
<dependencies>
...
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
...
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Service A
- Thêm dependencies sử dụng trong grpc
- spring-cloud-starter-openfeign: http client
- grpc-spring-boot-starter: grpc client
<properties>
<net.devh.grpc.starter.version>2.12.0.RELEASE</net.devh.grpc.starter.version>
</properties>
<dependencies>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
<version>${net.devh.grpc.starter.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-grpc</artifactId>
</dependency>
</dependencies>
Mặc định OpenFeign tích hợp sẵn sleuth, còn với gRPC cần sử dụng thêm dependency brave-instrumentation-grpc
- Cấu hình sleuth trong application.yml
spring:
sleuth:
propagation:
type: B3 #,W3C
sampler:
probability: 1.0
- Cấu hình service B và C trong application.yml
serviceb:
url: http://localhost:8082
path:
greet: /greet/{id}
grpc:
client:
servicec:
address: static://localhost:8183
enableKeepAlive: true
keepAliveWithoutCalls: true
negotiationType: plaintext
- Sử dụng FeignClient để tạo HTTP client gọi đến ServiceB
@FeignClient(value = "serviceb", url = "${serviceb.url}")
public interface ServiceBClient {
@RequestMapping(method = RequestMethod.GET, value = "${serviceb.path.greet}")
public MessageB greet(@PathVariable(name = "id") int userId);
}
- Viết gRPC client gọi đến ServiceC
import net.devh.boot.grpc.client.inject.GrpcClient;
...
@GrpcClient("servicec")
private ServiceCBlockingStub serviceCBlockingStub;
@Override
public MessageC greet(int id) {
GreetRequest request = GreetRequest.newBuilder()
.setId(id)
.build();
GreetResponse response = this.serviceCBlockingStub.greet(request);
return new MessageC(response.getMessage());
}
Service B
- Cấu hình port HTTP
server:
port : 8082
- Viết Rest controller đơn giản
@RestController
public class Controller {
@GetMapping("/greet/{id}")
public MessageB greet(@PathVariable(name = "id") int id) {
log.info("ServiceB.greet");
return new MessageB("MessageB greet " + id);
}
}
ServiceC
- Cấu hình port gRPC trong file application.yml
grpc:
server:
port: 8183
- Viết gRPC controller
import net.devh.boot.grpc.server.service.GrpcService;
...
@GrpcService
public class GrpcController extends ServiceCGrpc.ServiceCImplBase {
@Override
public void greet(GreetRequest request, StreamObserver<GreetResponse> responseObserver) {
log.info("ServiceC.greet " + request.getId());
GreetResponse response = GreetResponse.newBuilder()
.setMessage("Message C")
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
Test sleuth
-
Start 3 service A, B, C
-
Gửi request test đến ServiceA, trong ServiceA sẽ gọi đến ServiceB
$ curl http://localhost:8081/greetb/1
Log service A
2021-11-20 21:01:03.834 INFO [service-A,48c90d4d61668476,48c90d4d61668476] 2433 --- [nio-8081-exec-1] i.c.servicea.entrypoint.Controller : ServiceA.greetB
và service B
2021-11-20 21:01:03.939 INFO [service-B,48c90d4d61668476,c73d7b8f5c34adc6] 2429 --- [nio-8082-exec-1] i.c.serviceb.entrypoint.Controller : ServiceB.greet
chung TraceId 48c90d4d61668476
- Gửi request test đến ServiceA, trong ServiceA sẽ gọi đến ServiceC
$ curl http://localhost:8081/greetc/1
Log service A:
2021-11-20 21:02:54.170 INFO [service-A,9b6f0e1e5e5ca2da,9b6f0e1e5e5ca2da] 2433 --- [nio-8081-exec-2] i.c.servicea.entrypoint.Controller : ServiceA.greetC
và service C
2021-11-20 21:02:54.340 INFO [service-C,9b6f0e1e5e5ca2da,9cd977fdc2849bb4] 2100 --- [ault-executor-2] i.c.servicec.entrypoint.GrpcController : ServiceC.greet 1
chung TraceId 9b6f0e1e5e5ca2da
Report lên Zipkin, Jaeger
- Setup Zipkin bằng docker-compose
version: "3.4"
services:
zipkin:
image: openzipkin/zipkin
ports:
- 9411:9411
Run docker-compose
docker-compose up -d
- Trong mỗi service, thêm thông tin dependency
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
</dependencies>
- Thêm cấu hình zipkin trong application.yml
spring:
zipkin:
base-url: http://localhost:9411
- Send request và mở Zipkin UI tại địa chỉ http://localhost:9411/ lên xem report


- Ngoài ra, có thể sử dụng Jeager server để hiện thị report
- Setup Jeager server bằng docker-compose và cho phép nhận report theo format của zipkin
version: "3.4"
services:
jaeger:
image: jaegertracing/all-in-one:1.17
ports:
- 5775:5775/udp
- 6831:6831/udp
- 6832:6832/udp
- 5778:5778
- 16686:16686
- 14268:14268
- 14250:14250
- 9411:9411
environment:
- COLLECTOR_ZIPKIN_HTTP_PORT=9411
Test và xem kết quả trên Jeager UI tại địa chỉ http://localhost:16686/


Reference
- https://spring.io/projects/spring-cloud-sleuth
- https://www.baeldung.com/spring-cloud-sleuth-single-application
- https://docs.spring.io/spring-cloud-sleuth/docs/current-SNAPSHOT/reference/html/integrations.html#sleuth-openfeign-integration
- https://github.com/yidongnan/grpc-spring-boot-starter
Source code ở đây