λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
  • μž₯원읡 κΈ°μˆ λΈ”λ‘œκ·Έ
πŸ’Š Java & Kotlin & Spring/- spring framework +

[Distributed Tracing] Messaging ν™˜κ²½μ—μ„œμ˜ λΆ„μ‚° 좔적 μ‹€μŠ΅

by Wonit 2022. 5. 1.

λ³Έ 글은 λ§ˆμ΄ν¬λ‘œμ„œλΉ„μŠ€μ˜ λΆ„μ‚° 좔적 μ‹œλ¦¬μ¦ˆλ‘œ 이둠과 μ‹€μŠ΅μ΄ ν•¨κ»˜ ν¬ν•¨λœ μ‹œλ¦¬μ¦ˆμž…λ‹ˆλ‹€. μ•„λž˜ λͺ©μ°¨μ— ν‘œμ‹œλœ 글을 λͺ¨λ‘ μ°Έκ³ ν•˜λ©΄ μ’‹μŠ΅λ‹ˆλ‹€.

 

λͺ©μ°¨

 

μ‹€μŠ΅μ— λŒ€ν•œ μ†ŒμŠ€μ½”λ“œλ₯Ό ν™•μΈν•˜μ‹œκ³  μ‹Άλ‹€λ©΄ μ‹€μŠ΅ githubμ—μ„œ ν™•μΈν•˜μ‹€ 수 μžˆμŠ΅λ‹ˆλ‹€.

 


Messaging ν™˜κ²½ μ—μ„œμ˜ λΆ„μ‚° 좔적 μ‹€μŠ΅

 

μ§€λ‚œ μ‹œκ°„ μš°λ¦¬λŠ” HTTP ν™˜κ²½μ—μ„œμ˜ λΆ„μ‚° 좔적 에 λŒ€ν•΄μ„œ μ•Œμ•„λ³΄μ•˜λ‹€.

 

λ‹€μ‹œ ν•œλ²ˆ 개랡적인 μ•„ν‚€ν…μ²˜μ— λŒ€ν•΄μ„œ ν™•μΈν•΄λ³΄μž

 

 

μœ„ μ•„ν‚€ν…μ²˜λ₯Ό κ΅¬λΆ„ν•˜μžλ©΄ 2개의 λΆ€λΆ„μœΌλ‘œ λ‚˜λˆŒ 수 μžˆλ‹€.

 

  1. HTTP API
  2. Messaging

 

μ΄λ²ˆμ—λŠ” HTTP 톡신을 λ„˜μ–΄ Messaging ν™˜κ²½μ—μ„œ Span 듀을 ν•˜λ‚˜μ˜ Trace 둜 관리할 수 μžˆλ„λ‘ ν•΄λ³Ό 것이닀.

 

  • user-service : express, node.js
  • order-service : spring boot, java
  • delivery-service : spring boot, java & aws sqs
  • notification-service : spring boot, java & aws sqs

 

λ§ˆμ΄ν¬λ‘œμ„œλΉ„μŠ€ 호좜 Flow

 

μ‹€μŠ΅μ— λŒ€ν•œ 호좜의 flow λ₯Ό λΉ„μ¦ˆλ‹ˆμŠ€μ μΈ ν•œ λ¬Έμž₯으둜 ν‘œν˜„ν•˜μžλ©΄ λ‹€μŒκ³Ό κ°™λ‹€

 

μ‚¬μš©μž (User) κ°€ μ£Όλ¬Έ (Order) 을 ν•œλ‹€λ©΄ μ‚¬μš©μžμ˜ 정보와 μ£Όλ¬Έ μƒν’ˆμ˜ 정보λ₯Ό ν† λŒ€λ‘œ 배솑 (Delivery) ν•˜κ³ , 배솑이 μ‹œμž‘λ˜μ—ˆλ‹€λŠ” μ•Œλ¦Ό (Notification) 을 μ „μ†‘ν•œλ‹€.

 

μœ„ 과정은 λ‹€μŒκ³Ό 같은 호좜이 ν•˜λ‚˜μ˜ νŠΈλžœμž­μ…˜μœΌλ‘œ 이루어진닀

 

  • user-service λŠ” order-service 둜 HTTP GET μš”μ²­μ„ 보낸닀
  • order-service λŠ” delivery-service 둜 HTTP POST μš”μ²­μ„ 보낸닀
  • deliver-service λŠ” HTTP μš”μ²­μ΄ λ°œμƒν•˜λ©΄ AWS SQS 둜 Message λ₯Ό produce ν•œλ‹€.
  • notification-service λŠ” Queue 에 λ©”μ‹œμ§€κ°€ μ‘΄μž¬ν•œλ‹€λ©΄ Message λ₯Ό Consume ν•œλ‹€

 

AWS SQS 에 Queue λ§Œλ“€κΈ°

 

μš°μ„  λ©”μ‹œμ§• ν™˜κ²½μ„ κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄μ„œ AWS SQS 에 큐 ν•˜λ‚˜λ₯Ό μƒμ„±ν•˜μž

 

이번 μ‹œκ°„μ—λŠ” Message Queue λ‚˜ AWS SQS 에 λŒ€ν•œ μ‹œκ°„μ΄ μ•„λ‹ˆκΈ° λ•Œλ¬Έμ— SQS κ΄€λ ¨λœ 섀정은 λ„˜μ–΄κ°€λ„λ‘ ν•˜κ² λ‹€.

 

큐가 μ€€λΉ„λ˜μ—ˆλ‹€λ©΄ 이제 Delivery Service μ—μ„œ 큐와 연동을 ν•΄λ³΄μž

 

Deliver-Service

 

deliver-service μ—μ„œ λ‹€μŒκ³Ό 같은 μ˜μ‘΄μ„±μ„ μΆ”κ°€ν•΄μ€€λ‹€.

 

build.gradle

// zipkin & sleuth
implementation 'org.springframework.cloud:spring-cloud-starter-sleuth:3.1.1'
implementation 'org.springframework.cloud:spring-cloud-sleuth-zipkin:3.1.1'
implementation 'io.zipkin.aws:brave-instrumentation-aws-java-sdk-sqs:0.23.4'

// aws sqs
implementation 'org.springframework.cloud:spring-cloud-aws-messaging:2.2.6.RELEASE'

 

Zipkin κ³Ό Sleuth 그리고 Brave Instrumentation 을 좔가해주도둝 ν•œλ‹€

 

μ—¬κΈ°μ„œ λˆˆμ—¬κ²¨ λ³Ό 섀정은 λ°”λ‘œ λ‹€μŒ 2κ°œμ΄λ‹€.

 

  • org.springframework.cloud:spring-cloud-aws-messaging
  • io.zipkin.aws:brave-instrumentation-aws-java-sdk-sqs:0.23.4

 

천천히 μ‚΄νŽ΄λ³΄μž

 

delivery-service μ—μ„œ Producer μ„€μ •ν•˜κΈ°

 

Producer λ₯Ό μ„€μ •ν•  λ•Œ 보톡 QueueMessagingTemplate 을 μ‚¬μš©ν•˜κ³€ ν•œλ‹€.

 

여기에 Trace 정보λ₯Ό μΆ”κ°€ν•˜κΈ° μœ„ν•΄μ„œλŠ” BraveInstrumentation 의 SqsMessagingTemplate 을 RequestHandler 둜 μΆ”κ°€ν•΄μ€˜μ•Ό ν•œλ‹€

 

@Configuration
public class SqsConfig {

    Tracer tracer;

    public SqsConfig(Tracer tracer) {
        this.tracer = tracer;
    }

    @Bean
    public QueueMessagingTemplate queueMessagingTemplate() {
        Tracing current = Tracing.current();

        SqsMessageTracing sqsMessageTracing = SqsMessageTracing.create(current);

        AmazonSQSAsync client = AmazonSQSAsyncClientBuilder.standard()
                .withRegion(Regions.AP_NORTHEAST_2)
                .withCredentials(new EnvironmentVariableCredentialsProvider())
                .withRequestHandlers(sqsMessageTracing.requestHandler())
                .build();

        return new QueueMessagingTemplate(client);
    }
}

 

Brave Instrumentation μ΄λž€ Zipkin μ„œλ²„λ‘œ Span 데이터λ₯Ό μ „μ†‘ν•˜λŠ” κ΅¬ν˜„μ²΄μ΄λ‹€.

 

Messaging ν™˜κ²½μ—μ„œλŠ” B3-Propagation 이 μž‘λ™ν•˜μ§€ μ•ŠλŠ”λ‹€.

 

Seamless ν•œ Tracing ν™˜κ²½μ„ λ§Œλ“€κΈ° μœ„ν•΄μ„œλŠ” Queue 둜 던질 Message 의 Header 에 B3 데이터λ₯Ό λ„£μ–΄μ€˜μ•Ό ν•˜λ―€λ‘œ Tracer 의 κ΅¬ν˜„μ²΄λ₯Ό 직접 닀뀄야 ν•œλ‹€.

 

κ·Έ 과정이 λ°”λ‘œ withRequestHandlers(sqsMessageTracing.requestHandler()) 이닀

 

또 λˆˆμ—¬κ²¨λ³Ό 점은 Tracing 을 Bean μ£Όμž…μ„ 톡해 κ°€μ Έμ˜€λŠ” 것이닀.

 

Span 에 λŒ€ν•œ Global ν•œ Access λ₯Ό μœ„ν•΄μ„œλŠ” μ•žμ„  Zipkin & Sleuth λ₯Ό μ„€λͺ…ν•˜λ©° 이야기λ₯Ό μž μ‹œ ν–ˆλ“―, Bean 으둜 μ£Όμž…λ°›μ•„μ•Ό ν•œλ‹€.

 

κ·Έλž˜μ•Ό Span 에 λŒ€ν•œ Lifecycle 에 λŒ€ν•œ 관리가 κ°€λŠ₯ν•˜κ²Œ λœλ‹€.

 

MessageSender.java

 

이제 Queue 섀정이 λλ‚¬μœΌλ‹ˆ Queue μ—κ²Œ Message λ₯Ό Send ν•  Sender λ₯Ό λ§Œλ“€μ–΄λ³΄μž

 

@Component
@RequiredArgsConstructor
public class MessageSender {
    @Value("${aws.queue.name}")
    private String notificationQueue;

    private final QueueMessagingTemplate template;

    public void send(Object message) {
        template.convertAndSend(notificationQueue, message);
    }
}

 

일반적으둜 SQS Sender λ₯Ό κ΅¬ν˜„ν•  λ•Œ μ‚¬μš©ν•˜λŠ” 방식과 λ™μΌν•˜λ‹€

 

그리고 DeliveryController 에 λ‹€μŒκ³Ό 같이 μš”μ²­μ„ λ°›μœΌλ©΄ Message λ₯Ό Produce ν•˜λŠ” λ‘œμ§μ„ μΆ”κ°€ν•΄λ³΄μž

 

DeliveryController

 

@RestController
@RequestMapping("/delivery")
@RequiredArgsConstructor
public class DeliveryController {

    private final MessageSender messageSender;

    @PostMapping
    public String delivery(@RequestBody DeliveryRequest request) {

        String userId = request.getUserId();
        String address = request.getAddress();

        messageSender.send("배솑 문자 λ°œμ†‘ μš”μ²­2");

        return "[delivery-service] userId: " + userId + " address: " + address + " 배솑 μš”μ²­ μ™„λ£Œ";
    }
}

 

이제 λ‹€μ‹œ Zipkin Dashboard λ₯Ό ν™•μΈν•΄λ³΄μž

 

 

그럼 μœ„μ™€ 같이 message publish 에 λŒ€ν•œ Span 이 μΆ”κ°€λœ 것을 λ³Ό 수 μžˆλ‹€.

 

Messaging ν™˜κ²½μ—μ„œλŠ” HTTP ν™˜κ²½κ³Ό λ‹€λ₯΄κ²Œ 2κ°€μ§€μ˜ Annotation 이 μ‘΄μž¬ν•œλ‹€.

 

 

  1. Producer Start
  2. Producer Finish

 

이것을 λ―Έλ£¨μ–΄λ³΄μ•˜μ„ λ•Œ, Messaging ν™˜κ²½μ—μ„œλŠ” Span 이 Close λ˜λŠ” μ‹œμ , 즉 Zipkin 으둜 Reporting ν•˜λŠ” μ‹œμ μ΄ Producer Finish ν•˜λŠ” μ‹œμ μž„μ„ μ•Œ 수 μžˆλ‹€.

 

이제 Consumer λ₯Ό λ§Œλ“€μ–΄λ³΄μž

 

Notification-Service

 

μ§€κΈˆ ν•΄λ³Ό 뢀뢄은 Consumer μ—μ„œ Consume ν•œ Message λ₯Ό Trace ν•˜λŠ” 것이닀

 

 

μœ„μ˜ 흰색 μ˜μ—­μ„ κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄μ„œ Notification Service λ₯Ό λ§Œλ“€μ–΄λ³΄μž

 

속도λ₯Ό λ‚΄μ„œ λΉ λ₯΄κ²Œ μ½”λ“œλ§Œ μš°μ„  보여주도둝 ν•˜κ² λ‹€

 

SqsConfig.java

@Configuration
public class SqsConfig {

    @Bean
    public QueueMessagingTemplate queueMessagingTemplate() {
        Tracing current = Tracing.current();
        SqsMessageTracing sqsMessageTracing = SqsMessageTracing.create(current);

        AmazonSQSAsync client = AmazonSQSAsyncClientBuilder.standard()
                .withRegion(Regions.AP_NORTHEAST_2)
                .withCredentials(new EnvironmentVariableCredentialsProvider())
                .withRequestHandlers(sqsMessageTracing.requestHandler())
                .build();

        return new QueueMessagingTemplate(client);
    }
}

 

SqsConfig μ—μ„œλŠ” Producer 와 λ§ˆμ°¬κ°€μ§€λ‘œ QueueMessagingTemplate 을 생성해쀄 λ•Œ, Tracer λ₯Ό ν•¨κ»˜ λΆ™μ—¬μ•Όν•œλ‹€

 

MessageReceiver.java

@Service
@RequiredArgsConstructor
public class MessageReceiver {

    @SqsListener("${aws.queue.name}")
    public void receiveMessage(String message) {
        System.out.println("λ©”μ‹œμ§€ μˆ˜μ‹  μ™„λ£Œ, message = " + message);
    }
}

 

MessageReceiver μ—μ„œλŠ” 일번적으둜 μ‚¬μš©ν•˜λŠ” reveicer 와 λ˜‘κ°™μ΄ κ΅¬ν˜„ν•΄μ£Όλ©΄ λœλ‹€.

 

이제 λͺ¨λ“  κ΅¬ν˜„μ΄ 끝났기 λ•Œλ¬Έμ— 전체적인 μš”μ²­μ„ λ‹€μ‹œ 보내보도둝 ν•˜μž

 

ν˜Ήμ‹œλΌλ„ μžŠμ–΄λ²„λ Έμ„ 수 μžˆμœΌλ‹ˆ μ‹€μŠ΅μ— λŒ€ν•œ 호좜의 flow λ₯Ό λΉ„μ¦ˆλ‹ˆμŠ€μ μΈ ν•œ λ¬Έμž₯으둜 ν‘œν˜„ν•΄λ³΄μžλ©΄,

 

μ‚¬μš©μž (User) κ°€ μ£Όλ¬Έ (Order) 을 ν•œλ‹€λ©΄ μ‚¬μš©μžμ˜ 정보와 μ£Όλ¬Έ μƒν’ˆμ˜ 정보λ₯Ό ν† λŒ€λ‘œ 배솑 (Delivery) ν•˜κ³ , 배솑이 μ‹œμž‘λ˜μ—ˆλ‹€λŠ” μ•Œλ¦Ό (Notification) 을 μ „μ†‘ν•œλ‹€.

 

이기 λ•Œλ¬Έμ— User-Service 둜 μš”μ²­μ„ 보내면 우리의 Producer, Consumer 듀이 잘 λ™μž‘ν•΄μ„œ Zipkin Dashboard 에 도착할 것이닀

 

 

localhost:8000 인 user-service 둜 μš”μ²­μ„ 보내면 λ‹€μŒκ³Ό 같은 정보듀이 zipkin dashboard 에 찍히게 λœλ‹€.

 

 

끝으둜..

 

μ΄λ ‡κ²Œ μš°λ¦¬λŠ” κ°„λ‹¨ν•œ μ‹€μŠ΅μ„ κ±°μ³μ„œ HTTP ν™˜κ²½κ³Ό Messaging ν™˜κ²½μ—μ„œμ˜ 데이터λ₯Ό Tracing ν•΄λ³΄μ•˜λ‹€.

 

μ•„μ‰¬μš΄ 점이 μžˆλ‹€λ©΄ μ–΄λ–€ λ°©μ‹μœΌλ‘œ SpanId κ°€ μƒμ„±λ˜κ³  λ™μž‘ν•˜λŠ”μ§€μ— λŒ€ν•΄μ„œ 깊게 μ•Œμ•„λ³΄μ§€ λͺ»ν–ˆλ˜ 것이 아쉽닀.

 

ν•˜μ§€λ§Œ λΆ„λͺ… 이 λ‹€μŒ μ‹œκ°„ μ–Έμ  κ°€λŠ” κ·Έ 뢀뢄을 μ΄μ•ΌκΈ°ν•˜λŠ” μ‹œκ°„μ΄ μžˆμ„κ²ƒμ΄λ‹€.

 

λŒ“κΈ€