(임시)[Spring] Spring 비동기 끄적이는중...

대기열 서비스 관련 키워드

캐싱(Redis), Spring Async, Message Queue(ex. RabbitMQ, Kafka)  로드 밸런싱

 

Spring 비동기 처리와 메시지 큐는 대규모 트래픽을 처리하는 복잡한 애플리케이션, 특히 병원 예약 및 관리 시스템과 같은 시스템에서 매우 중요한 기술입니다. 이들 기술은 애플리케이션의 성능을 향상시키고, 사용자 경험을 개선하며, 시스템의 확장성과 유연성을 증가시키는 데 도움을 줍니다.

 

Spring 비동기 처리의 필요성

  • 성능 향상: Spring의 @Async 어노테이션을 사용한 비동기 메소드 호출은 병렬 처리를 가능하게 하여 처리 속도를 향상시킵니다. 예를 들어, 사용자의 예약 요청 처리 중에 SMS 알림을 보내는 작업을 비동기적으로 처리할 수 있습니다. 이는 메인 작업 흐름을 차단하지 않으므로 사용자에게 더 빠른 응답 시간을 제공합니다.
  • 리소스 활용 최적화: 동기 처리 방식은 작업을 처리하는 동안 리소스(예: 스레드)를 차단할 수 있습니다. 비동기 처리를 사용하면 이러한 리소스가 효율적으로 사용되어 시스템 전체의 부하를 줄일 수 있습니다.

만약 알림 발송 작업을 비동기가 아닌 동기적으로 처리할 경우엔 어떻게 될까?

  1. SMS 알림 발송 작업을 동기적으로 처리할 경우, 사용자의 예약 요청을 처리하는 동안 알림 발송 로직이 완료될 때까지 전체 작업이 차단됩니다. 이는 몇 가지 문제를 발생시킬 수 있습니다:
    • 응답 시간 증가: 사용자가 예약을 요청하고 그 응답을 받기까지의 시간이 길어집니다. SMS 발송 과정에서 네트워크 지연이나 외부 시스템의 지연이 발생할 경우, 이 문제는 더욱 심각해질 수 있습니다.
      • 동기적으로 알림(예: SMS)을 발송하는 경우, '응답을 받기까지 기다린다'는 것은 네트워크 요청을 통한 알림 서비스(예: SMS 게이트웨이)로부터 성공적으로 메시지가 전송되었음을 알리는 서버 측 응답을 의미합니다. 사용자가 실제로 알림을 확인하는 것이 아니라, 알림 발송 요청이 SMS 서비스 제공업체 등의 외부 시스템에 의해 처리되었고, 그 처리 결과(성공, 실패, 오류 코드 등)가 시스템에 반환될 때까지 기다리는 과정을 말합니다. 실제 사용자가 알림을 확인하는 시점은 서버 측에서 알 수 없으며, 이는 동기 처리 과정에서 기다리는 대상이 아닙니다. 동기 처리에서는 단지 외부 시스템의 처리 완료 응답을 기다리는 것이지, 사용자의 알림 확인까지 기다리지는 않습니다.  
      • '응답을 받는다'의 기준은, 따라서 알림을 발송하는 시스템이 외부 서비스(예: 알림 서비스 제공업체)로부터 알림 발송 요청에 대한 처리 결과를 받는 순간입니다. 이는 HTTP 상태 코드, 성공 여부, 오류 메시지 등의 형태로 반환될 수 있으며, 이 정보를 바탕으로 애플리케이션이 다음 단계의 로직을 진행할 수 있습니다. 예를 들어, 알림 발송 요청이 성공적으로 처리되었다는 응답을 받으면, 시스템은 사용자에게 예약이 완료되었다는 응답을 전달할 수 있습니다. 반면, 알림 발송이 실패했다는 응답을 받으면, 이에 대한 오류 처리 로직을 수행할 수 있습니다.
    • 리소스 효율성 감소: 동기 처리 방식은 요청을 처리하는 동안 해당 스레드가 차단되기 때문에, 스레드의 활용도가 낮아지고 시스템 리소스가 낭비될 수 있습니다.
    • 사용자 경험 저하: 사용자가 시스템의 응답을 기다리는 시간이 길어지면, 사용자 경험이 저하됩니다. 특히, 빠른 응답을 기대하는 웹 환경에서는 사용자 만족도에 큰 영향을 줄 수 있습니다.
  2. 비동기 처리의 실행 방식:
    • 트리거를 통한 별도 실행: 비동기 메소드가 호출되면, 메인 작업 흐름(예: 사용자의 예약 요청 처리)은 비동기 작업(예: SMS 알림 발송)의 완료를 기다리지 않고 계속 진행됩니다. 이는 Node.js에서 이벤트 루프와 비슷하게, 메인 작업 흐름이 차단되지 않도록 하여, 동시에 여러 작업을 효율적으로 처리할 수 있게 합니다.
    • 동기 처리 후 비동기 작업 실행: 비동기 작업은 동기 작업이 모두 완료된 이후에 실행되는 것이 아니라, 비동기 메소드 호출 즉시 별도의 스레드에서 실행을 시작합니다. 이는 동기 작업의 처리와는 독립적으로 진행되므로, 동기 작업의 완료 여부와 관계없이 비동기 작업을 수행할 수 있습니다.
  3. Spring에서 비동기 작업을 처리하는 방식은 일반적으로 Node.js의 이벤트 기반 모델과는 조금 다릅니다. Spring의 비동기 처리는 @Async 어노테이션을 사용하여 비동기 메소드를 정의하고, 이 메소드가 호출될 때 Spring Framework가 자동으로 별도의 스레드(스레드 풀 내)에서 해당 작업을 비동기적으로 실행합니다. 이 방식에 따른 처리 흐름은 다음과 같습니다:

따라서, 비동기 처리는 메인 작업 흐름의 차단 없이 동시에 여러 작업을 효율적으로 처리하며, 시스템의 성능과 사용자 경험을 향상시키는 중요한 역할을 합니다. 이는 트랜잭션 기반의 동기 처리 완료 후에 비동기 작업을 실행하는 것이 아니라, 필요에 따라 별도의 스레드에서 동시에 실행되는 방식입니다.

알림 서비스 = 외부 API와 같이 동기적인 처리는 매우 비효율!!!

아 이해했어. 결국은 이메일 알림도 외부 API를 이용하듯이 많은 작업량 혹은 시간이 할애 되기에 이를 비동기로 처리함으로서 불필요한 대기 시간을 없애는 것이 서비스의 향상에 좋다는 말인거지?

 

맞습니다. 외부 API를 사용하는 이메일 알림과 같은 작업은 처리에 상당한 시간이 걸릴 수 있으며, 이를 동기적으로 처리할 경우 전체 시스템의 응답성이 저하될 위험이 있습니다. 사용자가 서비스에 요청을 한 후에 필요한 작업(예: 예약 확인, 이메일 알림 발송 등)을 처리하면서 서버가 그 응답을 완료할 때까지 사용자가 불필요하게 기다려야 하는 상황이 발생할 수 있습니다.

이를 비동기적으로 처리하면, 이메일 알림 발송과 같은 시간이 많이 소요되는 작업을 백그라운드에서 실행시키고, 그 완료를 기다리지 않고도 다른 작업을 계속 진행할 수 있습니다. 이 방식은 다음과 같은 장점을 제공합니다:

  1. 응답 시간 개선: 사용자가 시스템에 요청을 한 후, 빠르게 응답을 받을 수 있어 사용자 경험이 크게 향상됩니다. 사용자는 시스템이 백그라운드에서 처리 작업을 계속하고 있음을 기다리지 않고, 다른 작업을 진행할 수 있습니다.
  2. 리소스 활용 최적화: 비동기 처리를 통해 시스템 리소스를 보다 효율적으로 사용할 수 있습니다. 필요한 작업을 별도의 스레드나 프로세스에서 처리하므로, 메인 스레드는 다른 요청을 더 빠르게 처리할 수 있게 됩니다.
  3. 시스템의 확장성 향상: 비동기 처리는 시스템의 확장성을 높여줍니다. 특히, 클라우드 환경과 같이 자원을 동적으로 할당할 수 있는 환경에서는, 필요에 따라 처리 능력을 쉽게 확장할 수 있습니다.
  4. 사용자 만족도 증가: 시스템의 응답 시간이 단축되고, 사용자가 빠른 피드백을 받을 수 있게 되면, 사용자 만족도가 크게 향상됩니다.

따라서, 이메일 알림과 같이 시간이 많이 소요되는 작업을 비동기적으로 처리하는 것은 서비스의 전반적인 성능 개선과 사용자 경험 향상에 크게 기여할 수 있습니다.

메시지 큐의 필요성

  • 분산 시스템 간의 결합도 감소: 메시지 큐를 사용하면 시스템 간의 결합도를 낮추고, 서비스의 독립성을 보장할 수 있습니다. 예를 들어, 예약 시스템과 결제 시스템이 서로 독립적으로 작동하면서도 메시지 큐를 통해 효율적으로 통신할 수 있습니다.
  • 탄력적인 작업 처리: 트래픽이 급증하는 경우, 메시지 큐는 요청을 임시 저장하고 시스템이 처리할 수 있는 속도로 순차적으로 처리합니다. 이는 시스템 오버로드를 방지하고 안정성을 보장합니다.
  • 비동기 통신: 다양한 서비스 간의 비동기 통신을 가능하게 하여, 한 서비스의 처리 시간이 다른 서비스에 영향을 미치지 않도록 합니다. 이는 전체 시스템의 응답성을 향상시킵니다.

실제 사용 예

  • 예약 처리: 사용자가 병원 예약을 요청하면, 이 요청은 비동기적으로 처리되어 사용자는 즉시 응답을 받을 수 있습니다. 동시에, 예약 정보는 메시지 큐를 통해 다른 시스템(예: 결제 시스템, SMS 알림 시스템)으로 전달될 수 있습니다.
  • 알림 서비스: 사용자가 예약한 시간에 대한 알림을 비동기적으로 처리하며, 메시지 큐는 알림을 순차적으로 발송하도록 보장합니다.
  • 결제 처리: 사용자의 결제 요청을 메시지 큐에 넣어 결제 시스템이 병목 현상 없이 순차적으로 처리할 수 있게 합니다.

이처럼 Spring 비동기 처리와 메시지 큐는 시스템의 성능과 안정성을 향상시키며, 사용자 경험을 개선하는 데 필수적인 기술입니다. 병원 예약 및 관리 시스템의 경우, 이 기술들은 시스템의 확장성과 유연성을 보장하며, 대규모 트래픽 상황에서도 안정적인 서비스 제공을 가능하게 합니다.

 

 

대기열 예약 시스템에 어떻게 해당 내용을 녹일 것인가.......

 
1.나는 대기열 시스템의 예약 기능을 어떻게 설계하고 개발할지를 지금 고민하고 있는데, 너가 앞서 말했던 비동기와 메시지 큐를 이용해야 할 것 같은 생각이 들었어.. 근데 다른 방법도 알고 싶어. 그것들을 알려줘.
2. 예를 들어 예약 내용들을 전부 db에 우선 저장한뒤에 나중에 예약 처리를 하는 것처럼 어떤 방법등이 있을지 너가 알려줘.

 

 
 

큐 시스템의 스케줄링 기능을 설계하고 개발하는 데 있어서 비동기 처리와 메시지 큐 외에도 고려할 수 있는 다양한 방법이 있습니다. 특히 예약 시스템과 같은 경우, 예약 상세 정보를 데이터베이스에 먼저 저장한 뒤 나중에 예약을 처리하는 방식 등 여러 가지 접근 방식을 사용할 수 있습니다.

1. 배치 처리 시스템 활용

예약 상세 정보를 데이터베이스에 저장한 후, 정해진 시간이나 특정 조건이 만족될 때 일괄적으로 처리하는 배치 처리 방식을 사용할 수 있습니다. 이 방법은 다음과 같은 상황에 적합합니다:

  • 대용량 데이터 처리: 한 번에 많은 양의 예약을 처리해야 할 때 효과적입니다.
  • 비동기 처리: 사용자 요청에 즉각적으로 응답한 뒤, 실제 예약 처리는 시스템의 부하가 낮은 시간에 배치 작업을 통해 수행될 수 있습니다.

2. 지연 처리 (Deferred Processing)

사용자로부터 예약 요청을 받았을 때, 즉시 예약을 확정하지 않고 일정 시간 뒤나 시스템이 특정 조건을 만족할 때까지 처리를 지연시키는 방법입니다. 지연 처리는 다음과 같은 경우 유용합니다:

  • 리소스 최적화: 시스템의 리소스를 효율적으로 사용하기 위해, 부하가 적은 시간에 예약 처리 작업을 집중시킬 수 있습니다.
  • 유연한 운영: 예약 조건이나 가용성에 따라 유동적으로 예약 처리를 조정할 수 있습니다.

3. 우선순위 큐 사용

예약 처리 시 우선순위를 기반으로 처리 순서를 결정하는 방법입니다. 예약의 긴급성, 고객의 중요도, 예약 시간 등 다양한 기준으로 우선순위를 설정할 수 있습니다. 우선순위 큐는 다음과 같은 상황에서 유리합니다:

  • 중요 예약 우선 처리: 특정 조건을 만족하는 예약을 더 빨리 처리해야 할 때.
  • 효율적인 리소스 관리: 우선순위에 따라 리소스를 할당하여 시스템의 전반적인 효율성을 높일 수 있습니다.

4. 이벤트 소싱 및 CQRS (Command Query Responsibility Segregation) 활용

이벤트 소싱은 시스템에서 발생하는 모든 이벤트(예: 예약 요청)를 순차적으로 로그에 기록하고, 이를 기반으로 시스템의 상태를 복원하는 방식입니다. CQRS는 명령(Command)과 조회(Query)를 분리하여 시스템의 복잡성을 관리하는 패턴입니다. 이 방법은 다음과 같은 이점을 제공합니다:

  • 확장성: 시스템을 수평적으로 확장하기 용이합니다.
  • 유연한 데이터 관리: 이벤트 로그를 기반으로 시스템의 상태를 다양한 방식으로 조회하거나 복원할 수 있습니다.

각 방법은 시스템의 요구 사항, 처리해야 할 데이터의 양, 사용자의 기대 응답 시간 등 다양한 요소를 고려하여 선택해야 합니다. 예약 시스템의 경우, 사용자 경험을 최우선으로 고려하면서도 시스템의 효율성과 확장성을 동시에 달성할 수 있는 방식을 선택하는 것이 중요합니다.

 

 

 

 

참고

Spring 비동기 처리를 큐 시스템과 결합하여 사용하는 것은 복잡한 애플리케이션에서 효율적인 작업 처리와 높은 확장성을 달성하는 데 도움이 됩니다. 아래는 Spring 비동기 처리를 큐 시스템에 적용하는 방법에 대한 개요입니다:

1. 비동기 작업 정의

Spring에서 @Async 어노테이션을 사용하여 비동기적으로 실행할 메서드를 정의합니다. 이러한 메서드는 별도의 스레드에서 실행되므로, 메인 애플리케이션 흐름을 차단하지 않습니다.

 

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncService {

    @Async
    public void sendMessages(String message) {
        // 메시지 전송 로직 구현
    }
}

2. 큐 시스템과의 통합

메시지 큐(예: RabbitMQ, Kafka 등)와 통합하여, 메시지를 큐에 삽입하거나 큐에서 메시지를 받아 처리하는 로직을 비동기 메서드 내에 구현합니다. 메시지 생산자(Producer)는 메시지를 큐에 삽입하고, 메시지 소비자(Consumer)는 큐에서 메시지를 비동기적으로 처리합니다.

메시지 생산자 예시:

@Service
public class MessageProducerService {

    public void sendMessageToQueue(String message) {
        // 메시지를 큐에 삽입하는 로직 구현
    }
}

메시지 소비자 예시:

@Service
public class MessageConsumerService {

    @Async
    public void processMessageFromQueue() {
        // 큐에서 메시지를 받아 처리하는 비동기 메서드
    }
}

3. 큐 메시지 처리

메시지 큐에서 메시지를 받아 처리할 때, 비동기 방식을 사용하면 메시지 처리 작업이 메인 애플리케이션 흐름을 방해하지 않으므로, 애플리케이션의 응답성이 개선됩니다. Spring의 이벤트 리스너나 메시지 리스너 컨테이너를 사용하여 메시지 큐에서 메시지를 비동기적으로 받고 처리할 수 있습니다.

4. 예외 처리 및 모니터링

비동기 작업에서는 예외 처리가 중요합니다. @Async 메서드는 Future<T>를 반환할 수 있으며, 이를 통해 비동기 작업의 결과나 예외를 처리할 수 있습니다. 또한, Spring Actuator와 같은 도구를 사용하여 실행 중인 비동기 작업을 모니터링하고 관리할 수 있습니다.

요약

Spring 비동기 처리를 큐 시스템과 결합하면, 메시지 기반의 비동기 통신을 통해 애플리케이션의 성능과 확장성을 향상시킬 수 있습니다. 이를 통해 시스템의 부하를 분산시키고, 사용자 요청 처리 시간을 최적화하여 전체 시스템의 효율성을 높일 수 있습니다.