[E-commerce App] Spring Cloud Sleuth + Zipkin을 이용한 마이크로 서비스의 분산 추척
이전 글에서는, 마이크로 서비스 간 통신에서 문제가 발생했을 때, circuit breaker를 사용해 연쇄적으로 문제가 발생하는 것을 방지하는 방법을 알아봤다.
이번에는, 하나의 어플리케이션이 기동될 때, 여러 마이크로 서비스를 사용할텐데,
이 때 해당하는 요청 정보가 어떻게 실행되고, 어느 단계를 거쳐 어떤 마이크로 서비스로 이동하는지 추적하는 방법에 대해 살펴보자.
이런 내용을 마이크로 서비스의 “분산 추적” 이라고 한다.
개요
Zipkin
- https://zipkin.io/
- Twitter에서 사용하는 분산 환경의 Timing 데이터 수집, 추적 시스템 (오픈소스)
- Google Drapper에서 발전하였으며, 분산 환경에서의 시스템 병목 현상 파악
- Collector, Query Service, Database, WebUI로 구성
- Span
- 하나의 요청에 사용되는 작업의 단위
- 64 bit unique ID - Trace
- 트리 구조로 이뤄진 Span 셋
- 하나의 요청에 대한 같은 Trace ID 발급
Zipkin 설치 및 실행
# 설치
$ curl -sSL https://zipkin.io/quickstart.sh | bash -s
# 실행
$ java -jar zipkin.jar
설치
실행
zipkin의 default 포트번호는 9411번이다.
http://127.0.0.1:9411
에 접속해보자.
Spring Cloud Sleuth
- 스프링 부트 애플리케이션을 Zipkin과 연동 -> 가지고 있는 로그 파일 데이터 혹은 스트리밍 데이터 값을 Zipkin에 전달하는 역할
- 요청 값에 따른 Trace ID, Span ID 부여
- Trace와 Span Ids를 로그에 추가 기능
- servlet filter
- rest template
- scheduled actions
- message channels
- feign client
Spring Cloud Sleuth + Zipkin
user-service
pom.xml
<!-- Zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
application.yml
spring:
zipkin:
base-url: http://127.0.0.1:9411
enabled: true
sleuth:
sampler:
probability: 1.0
UserServiceImpl.java
@Service
@Slf4j
public class UserServiceImpl implements UserService {
...
@Override
public UserDto getUserByUserId(String userId) {
UserEntity userEntity = userRepository.findByUserId(userId);
UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);
log.info("Before call orders microservice"); // 🌟 로그 생성
CircuitBreaker circuitbreaker = circuitBreakerFactory.create("circuitbreaker");
List<ResponseOrder> orderList = circuitbreaker.run(() -> orderServiceClient.getOrders(userId),
throwable -> new ArrayList<>());
log.info("After called orders microservice"); // 🌟 로그 생성
userDto.setOrders(orderList);
return userDto;
}
}
order-service
pom.xml
이전에 user-service
와 마찬가지로 두 가지 dependency를 추가한다.
application.yml
이전에 user-service
와 마찬가지로 zipkin과 sleuth 설정 정보를 추가한다.
OrderController.java
@RestController
@RequestMapping("/order-service")
@Slf4j
public class OrderController {
@PostMapping("/{userId}/orders")
public ResponseEntity<ResponseOrder> createOrder(@PathVariable("userId") String userId, @RequestBody RequestOrder requestOrder) {
log.info("Before add orders data"); // 🌟 로그 생성
...
log.info("After added orders data"); // 🌟 로그 생성
return ResponseEntity.status(HttpStatus.CREATED).body(responseOrder);
}
@GetMapping("/{userId}/orders")
public ResponseEntity<List<ResponseOrder>> getOrder(@PathVariable("userId") String userId) {
log.info("Before retrieve orders data"); // 🌟 로그 생성
...
log.info("After retrieved orders data"); // 🌟 로그 생성
return ResponseEntity.status(HttpStatus.OK).body(result);
}
}
테스트
이제 아래 순서대로 서버를 기동하자.
- eureka 서버
- rabbitmq 서버
- configuration 서버 (
config-service
) - gateway 서버 (
gateway-service
) - zipkin 서버
user-service
order-service
이제 상품을 두 번 주문한 뒤, order-service
의 인텔리제이 콘솔을 확인하자.
이 때, 앞에 있는게 trace id
이고 뒤에 있는게 order id
이다.
trace id
를 복사해 웹 브라우저(http://localhost:9411
)에서 확인해보자.
그럼 위와 같은 정보를 얻을 수 있다.
이제 사용자 단건 조회를 해보자.
요청을 한 뒤, user-service
의 인텔리제이 콘솔을 확인하자.
아까 order-service
에서 확인한 trace id
와 user-service
에 찍힌 trace id
가 같음을 확인할 수 있다.
같은 trace id
를 가진다는 것은 같은 요청이라는 의미이다.
이제 이 정보를 아까처럼 웹 브라우저에서 확인해보자.
user-service
에서 get 요청을 한 뒤, order-service
를 호출한 것을 알 수 있다. (사용자 주문 내역을 가져오기 위해)
이번에는 다른 기능을 이용해보자.
상단에 보이는 Find a trace
버튼을 클릭 -> +
버튼 클릭 -> serviceName
탭을 클릭 -> user-service
선택 -> RUN QUERY
이는 유저 서비스를 3번에 걸쳐 호출되었고, 오더 서비스를 1번 호출되었다는 의미이다.
이번에는 Find a trace
옆에 있는 Dependencies
를 클릭해보자.
이는 엮여있는 서비스들을 나타내며, order-service
를 클릭하게 되면, 몇 번의 호출 중 몇 번 에러가 발생했는지 나타난다.
오류 테스트
마지막으로, 오류가 발생했을 때를 가정해보자.
order-service
OrderController.java
@RestController
@RequestMapping("/order-service")
@Slf4j
public class OrderController {
...
@GetMapping("/{userId}/orders")
public ResponseEntity<List<ResponseOrder>> getOrder(@PathVariable("userId") String userId) {
...
/* 오류 테스트 */
try {
Thread.sleep(1000);
throw new Exception("장애 발생");
} catch(InterruptedException ex) {
log.warn(ex.getMessage());
}
log.info("After retrieved orders data"); // 🌟 로그 생성
return ResponseEntity.status(HttpStatus.OK).body(result);
}
}
이제 order-service
재기동 후 다시 주문을 해보자.
그리고 다시 사용자 단건 조회를 하게되면, order-service
에서 예외가 발생했기 때문에 주문 목록이 비어있을 것이다.
이제 웹 브라우저를 확인해보자.
user-service
의 콘솔에서 가장 최근 요청(사용자 단건조회)의 trace id
를 복사한 뒤 검색해보자.
그럼 위 사진처럼 붉은 색으로 오류가 발생했음을 알 수 있고, ORDER-SERVICE
를 클릭해보면, 아까 OrderContoller
에 정의한 “장애 발생” 문구를 확인할 수 있다.
그리고 Find a trace
에서 order-service
를 검색해보면,
역시 붉은 색으로 오류가 발생했음을 알 수 있다.
이번엔 Dependencies
에서 확인해보자.
오류가 발생한 건 수를 확인할 수 있다.
💛 개인 공부 기록용 블로그입니다. 👻