๊ณต๋ถ€/MSA

[MSA] Spring Cloud๋กœ ๊ฐœ๋ฐœํ•˜๋Š” ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(MSA) - ๋ถ„์‚ฐ ์ถ”์ (Zipkin)

sesam 2024. 3. 23. 04:33
728x90

 

 

๐Ÿ’ก Zipkin
ํ™ˆํŽ˜์ด์ง€ ๋งํฌ
๋ถ„์‚ฐ ์ถ”์ ์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ถ”์  ์ •๋ณด ์ฆ‰ ํŠธ๋ ˆ์ด์‹ฑ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ์„œ๋น„์Šค๊ฐ€ Zipkin์ด๋‹ค.

- Twitter์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ถ„์‚ฐ ํ™˜๊ฒฝ์˜ Timing ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘, ์ถ”์  ์‹œ์Šคํ…œ (์˜คํ”ˆ์†Œ์Šค)
- Google Drapper์—์„œ ๋ฐœ์ „ํ•˜์˜€์œผ๋ฉฐ, ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ์˜ ์‹œ์Šคํ…œ ๋ณ‘๋ชฉ ํ˜„์ƒ ํŒŒ์•…
- collerctor, Query Service, databaem WebUI๋กœ ๊ตฌ์„ฑ

Span
- ํ•˜๋‚˜์˜ ์š”์ฒญ์— ์‚ฌ์šฉ๋˜๋Š” ์ž‘์—… ๋‹จ์œ„
- 64 bit unique ID

Trace
- ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ์ด๋ฃจ์–ด์ง„ Span set
- ํ•˜๋‚˜์˜ ์š”์ฒญ์— ๋Œ€ํ•œ ๊ฐ™์€ Trace ID ๋ฐœ๊ธ‰

 

 

๐Ÿ’ก Spring Cloud Sleuth
ํ™ˆํŽ˜์ด์ง€ ๋งํฌ
๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๊ฐ€ ์—ฌ๋…ˆ๋˜์–ด ์žˆ๋Š” ์ƒํƒœ ๊ฐ’์„ ์ถ”์ ํ•ด์„œ ๋ˆ„๊ฐ€ ๋ˆ„๊ตฌ๋ฅผ ํ˜ธ์ถœํ–ˆ๊ณ  ์‹œ๊ฐ„์ด ์–ผ๋งˆ๋‚˜ ๊ฑธ๋ ธ์œผ๋ฉด ์ •์ƒ ์ƒํƒœ์ธ์ง€ ๋น„์ •์ƒ ์ƒํƒœ์ธ์ง€๋ฅผ ์•Œ๋ ค์ฃผ๊ณ  ์‹œ๊ฐํ™” ์‹œ์ผœ์ฃผ๋Š” ๋„๊ตฌ

- ์Šคํ”„๋ง ๋ถ€ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ Zipkin๊ณผ ์—ฐ๋™

- ์š”์ฒญ ๊ฐ’์— ๋”ฐ๋ฅธ Trace ID, Span ID ๋ถ€์—ฌ
- Trace์™€ Span IDs๋ฅผ ๋กœ๊ทธ์— ์ถ”๊ฐ€ ๊ฐ€๋Šฅ
    - servlet filter
    - rest template
    - scheduled actions
    - message channels
    - feign client

 

Zipkin์€ ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค ๊ฐ„์˜ ์ˆœ์ฐจ์ ์ธ ๋ฐ์ดํ„ฐ ํ˜ธ์ถœ๊ด€๊ณ„์— ๋Œ€ํ•ด ๋ฐ์ดํ„ฐ ์ด๋ ฅ์„ ๋ณด๊ด€(ํŠธ๋ ˆ์ด์‹ฑ)ํ•˜๊ณ 
Sluth๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ ๋ฐœ์ƒํ–ˆ๋˜ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ ๊ฐ’์„ ์งํ–‰ ์„œ๋ฒ„๋กœ ์ „๋‹ฌ์‹œ์ผœ์ฃผ๋Š” ์—ญํ• 

 

 

 


 

 

Zipkin ์„ค์น˜

  • Zipkin ์„ค์น˜

Zipkin ์„ค์น˜๋ฐฉ๋ฒ• ๋งํฌ

curl -sSL https://zipkin.io/quickstart.sh | bash -s

 

zipkin.jar ํŒŒ์ผ์ด ์„ค์น˜๋˜์—ˆ๋‹ค.

 

  • ํŒŒ์ผ ์‹คํ–‰
 java -jar zipkin.jar

 

  • ํฌํŠธ๋ฒˆํ˜ธ 9411๋กœ ๋“ค์–ด๊ฐ€ ๋ณด์ž.

 


 

 

Spring Cloud Sleuth + Zipkin์„ ์ด์šฉํ•œ Microservice์˜ ๋ถ„์‚ฐ ์ถ”์ 

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:
  application:
    name: user-service
  zipkin:
    base-url: http://localhost:9411
    enabled: true #zipkin ์„œ๋ฒ„ ์ž‘๋™ O
  sleuth:
    sampler:
      probability: 1.0 # ๋ฐœ์ƒํ•œ ๋กœ๊ทธ๋ฅผ 100%๋ณด์—ฌ์ฃผ๊ฒ ๋‹ค.

 

  • UserServiceImpl

circuitBreaker ์ž‘์„ฑํ•œ ๊ณณ์— log ์ถ”๊ฐ€(@Slf4j ์‚ฌ์šฉ)

@Override
public UserDto getUserByUserId(String userId) {
    UserEntity userEntity = userRepository.findAllByUserId(userId);

    if (userEntity == null)
        throw new UsernameNotFoundException("User not found");

    UserDto userDto = new ModelMapper().map(userEntity, UserDto.class); // (๋ฐ”๊พธ๊ณ  ์‹ถ์€ ๋ณ€์ˆ˜, ๋ฐ”๊พธ๊ณ  ์‹ถ์€ "ํด๋ž˜์Šค")

// 	...์ƒ๋žต...

/* ErrorDecoder */
        log.info("Before call orders microservice"); ๐Ÿ‘€ ์—ฌ๊ธฐ! log ์ถ”๊ฐ€!!
        CircuitBreaker circuitbreaker = circuitBreakerFactory.create("circuitbreaker"); // circuitbreaker ์ƒ์„ฑ
        List<ResponseOrder> orderList = circuitbreaker.run(() -> orderServiceClient.getOrder(userId), throwable -> new ArrayList<>()); // circuitbreaker ์‹คํ–‰
        // run(() -> ์ •์ƒ์ž‘๋™ํ•  ๊ฒฝ์šฐ ๋ฐ˜ํ™˜๊ฐ’, ๋ฌธ์ œ ์ƒ๊ฒผ์„ ๊ฒฝ์šฐ ๋ฐ˜ํ™˜๊ฐ’)
        log.info("After call orders microservice"); ๐Ÿ‘€ ์—ฌ๊ธฐ! log ์ถ”๊ฐ€!!
        userDto.setOrders(orderList);

        return userDto;
    }

 

 

 

Order Service

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:
  application:
    name: user-service
  zipkin:
    base-url: http://localhost:9411
    enabled: true #zipkin ์„œ๋ฒ„ ์ž‘๋™ O
  sleuth:
    sampler:
      probability: 1.0 # ๋ฐœ์ƒํ•œ ๋กœ๊ทธ๋ฅผ 100%๋ณด์—ฌ์ฃผ๊ฒ ๋‹ค.

 

  • OrderServiceImpl

log ์ถ”๊ฐ€(@Slf4j ์‚ฌ์šฉ)

@PostMapping("/{userId}/orders")
public ResponseEntity<ResponseOrder> createOrder(@PathVariable("userId") String userId, @RequestBody RequestOrder orderDetails) {
    log.info("Before add orders data"); ๐Ÿ‘€ ์—ฌ๊ธฐ! log ์ถ”๊ฐ€!!

    ModelMapper mapper = new ModelMapper();
    mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);


    OrderDto orderDto = mapper.map(orderDetails, OrderDto.class);
    orderDto.setUserId(userId);
    /* jpa */
    OrderDto createdOrder = orderService.createOrder(orderDto);
    ResponseOrder responseOrder = mapper.map(createdOrder, ResponseOrder.class);
    
    log.info("After add orders data"); ๐Ÿ‘€ ์—ฌ๊ธฐ! log ์ถ”๊ฐ€!!
    return ResponseEntity.status(HttpStatus.CREATED).body(responseOrder);
    }

 

 

 


 

 

 

Test 1

PostMan์—์„œ ํšŒ์›๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ ํ›„ ํšŒ์›์ •๋ณด ์กฐํšŒ ์‹คํ–‰

 

 

ํšŒ์›์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋Š” ํ•˜๋‚˜์˜ ์š”์ฒญ์ด๋ฏ€๋กœ ํ•˜๋‚˜์˜ trace ID์ด๋‹ค.

๋™์ผํ•œ trace ID์™€ ๋‹ค๋ฅธ span ID์ธ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Zipkin server์—์„œ ํ™•์ธํ•ด๋ณด์ž(port : 9411)

๊ฒ€์ƒ‰๋ฐฉ๋ฒ• โ‘ 

trace ID๋ฅผ ๊ฒ€์ƒ‰ํ•ด์„œ ์ •๋ณด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

Zipkin์€ ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค ๊ฐ„์˜ ์ˆœ์ฐจ์ ์ธ ๋ฐ์ดํ„ฐ ํ˜ธ์ถœ๊ด€๊ณ„์— ๋Œ€ํ•ด ๋ฐ์ดํ„ฐ ์ด๋ ฅ์„ ๋ณด๊ด€(ํŠธ๋ ˆ์ด์‹ฑ)ํ•˜๊ณ 
Sluth๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ ๋ฐœ์ƒํ–ˆ๋˜ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ ๊ฐ’์„ ์งํ–‰ ์„œ๋ฒ„๋กœ ์ „๋‹ฌ์‹œ์ผœ์ฃผ๋Š” ์—ญํ• 

 

 

๊ฒ€์ƒ‰๋ฐฉ๋ฒ• โ‘ก

ServiceName์œผ๋กœ๋„ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

๊ฒ€์ƒ‰๋ฐฉ๋ฒ• โ‘ข

Dependencies๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋‚ ์งœ๋ณ„๋กœ ํ˜ธ์ถœ์ •๋ณด๋ฅผ ์‹œ๊ฐํ™”ํ•ด์„œ ๋ณด์—ฌ์ค€๋‹ค.

์—๋Ÿฌ์—ฌ๋ถ€๊ฐ€ ์žˆ์—ˆ๋Š”์ง€๋„ ํ™•์ธ ๊ฐ€๋Šฅํ•œ ๋Œ€์‰ฌ๋ณด๋“œ์ด๋‹ค.

 

 

 

 

Test 2 ์˜ค๋ฅ˜ ๊ฐ€์ •

๊ฐ•์ œ๋กœ ์˜ค๋ฅ˜ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ๋ฅผ ๊ฐ€์ •ํ•ด๋ณด์ž!

Order Serivce

  • OrderController
@SneakyThrows
@GetMapping("/{userId}/orders")
public ResponseEntity<List<ResponseOrder>> getOrder(@PathVariable("userId") String userId) {
    log.info("Before retrieve orders data");
    Iterable<OrderEntity> orderList = orderService.getOrdersByUserId(userId);

    List<ResponseOrder> result = new ArrayList<>();
    orderList.forEach(v -> result.add(new ModelMapper().map(v, ResponseOrder.class)));

    try {   // ๐Ÿ‘€ ์—ฌ๊ธฐ! ์˜ค๋ฅ˜ ์ถ”๊ฐ€
        Thread.sleep(1000);
        throw new Exception("์žฅ์•  ๋ฐœ์ƒ");
    } catch(InterruptedException ex) {
        log.warn(ex.getMessage());
    }

    log.info("After retrieve orders data");
    return ResponseEntity.status(HttpStatus.OK).body(result);
}

 

 

Zipkin ์„œ๋ฒ„์—์„œ ํ™•์ธํ•ด๋ณด์ž

์ ์šฉํ–ˆ๋˜ ์˜ค๋ฅ˜๋ฅผ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Dependencies ํ™•์ธ

 

 

 

 

 

 

 

 

 

๋งˆ๋ฌด๋ฆฌ

zipkin์„ ํ™œ์šฉํ•˜๋ฉด ์–ด๋А ๊ธฐ๋Šฅ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์ฝ˜์†”์— ํ•˜๋‚˜ํ•˜๋‚˜ ์ฐ์–ด ํ™•์ธํ•˜์ง€ ์•Š์•„๋„, ์‰ฝ๊ฒŒ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ข‹๋‹ค.

1. ์—๋Ÿฌ๊ฐ€ ์–ด๋””์„œ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ์–ด์„œ๋„ ์ข‹์ง€๋งŒ(๋ถ„์‚ฐ์ถ”์ )

2. ํ˜ธ์ถœ ๋‚ด์—์„œ ์ข…์†์„ฑ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์–ด์„œ๋„ ์ข‹๋‹ค! ํ•œ๊ฐœ์˜ ํ˜ธ์ถœ์— ํ•ด๋‹นํ•˜๋Š” ๊ด€๊ณ„๋ฅผ trace ID๋กœ ์•Œ ์ˆ˜ ์žˆ๋‹ค.(์„œ๋น„์Šค ๋งคํ•‘)

3. ์ด๋ฅผ ์‹œ๊ฐํ™”ํ•ด์„œ ๋ณผ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ ์ดํ•ดํ•˜๊ธฐ ๋”์šฑ ์ข‹๋‹ค!(์‹œ๊ฐํ™”)

์ด ์™ธ์˜ ์žฅ์ ๋„ ์กด์žฌํ•œ๋‹ค.

 

 

 

 

ํ•œ ๋ฐœ ๋” ๋‚˜์•„๊ฐ€ ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๊ฐ€ ํ˜„์žฌ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ƒํƒœ๋‚˜ ํ˜ธ์ถœ๋„๋‹ˆ ์ •ํ™•ํ•œ ํšŸ์ˆ˜๋Š” ์ถ”๊ฐ€์ ์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋ง์ด ๊ธฐ๋Šฅ์„ ๋„ฃ์–ด์•ผ ํ•œ๋‹ค! ๋‹ค์Œ ๊ฒŒ์‹œ๋ฌผ ์ฐธ๊ณ !

 

 

 


 

 

 

๐Ÿ”ฅ Spring Boot 3.2 + Spring Cloud 2023.0.0 ๋ฒ„์ „ ์ด์Šˆ

๋ฒ„์ „์„ ๋†’์•„์ง„ ์ƒํƒœ์—์„œ๋Š” Dendency ๋“ฑ ๋งŽ์€๊ฒŒ ๋ฐ”๋€ ๋“ฏ ํ•˜๋‹ค.

์œ„์— ์„ค์ •์€ ์˜ˆ์ „ ๋ฒ„์ „์œผ๋กœ ์ตœ์‹  ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜ ๋งํฌ๋ฅผ ํ™•์ธํ•ด์„œ ๋‹ค์‹œ ์ง„ํ–‰ํ•ด์•ผํ•œ๋‹ค.

 

์ฐธ๊ณ ๋งํฌ 1

์ฐธ๊ณ ๋งํฌ 2

์ฐธ๊ณ ๋งํฌ 3