[CS공부] 비동기 연동, 언제 어떻게 써야 할까

백엔드 취준생·2025년 8월 31일
0

CS공부

목록 보기
12/13

동기 연동과 비동기 연동

동기

  • 순차적으로 실행
  • 한 작업이 끝날 때까지 다음 작업이 진행되지 않는다
  • 흐름을 직관적으로 이해 가능
  • 디버깅 용이

비동기

  • 한 작업이 끝날 때까지 기다리지 않고 바로 다음 작업을 진행할 수 있음
  • 사용 가능한 경우
    • 연동에 약간의 시차가 생겨도 문제가 되지 않을 때
    • 일부 기능은 실패했을 때 재시도 가능할 때
    • 연동에 실패했을 때 나중에 수동으로 처리할 수 있는 기능
    • 연동에 실패했을 때 무시해도 되는 기능
  • 후처리를 용이하게 할 수 있게 된다.

별도 스레드로 실행하기

비동기 연동을 하는 가장 쉬운 방법

  • 스프링 프레임워크는 @Async 를 이용한 비동기 실행 기능 제공
    • 특정 메소드를 비동기로 실행
  • 트랜잭션 문제
    • 예외 사항인데도 롤백이 안되는 경우
    • 비동기로 실행되어 익셉션이 전파되지 않았음
    • 별도 스레드로 실행되는 메소드를 같은 스레드에서 실행되도록 수정
    • 그 후 다시 비동기로 실행할 수 잇는 방법을 찾아 적용

메시징

서로 다른 시스템 간에 비동기로 연동할 때 주로 사용하는 방식

장점

  • 두 시스템이 서로 영향을 주지 않는다
  • 확장이 용이하다

단점

  • 구조가 복잡해진다

종류

  • 카프카, 래빗MQ, 레디스 pub/sub 등

카프카 고려사항

  • 높은 처리량 자랑 → 초당 백 만 개 이상의 메시지를 처리할 수 있다
  • 수평 확장 용이 → 서버, 파티션, 소비자를 늘리면 된다
  • 카프카는 메시지를 파일에 보관해서 메시지가 유실되지 않는다
  • 1개의 토픽이 여러 파티션을 가질 수 있는데, 팥티션 단위로 순서를 보장, 토픽 수준에서는 순서 보장할 수 없다.
  • 소비자는 메시지를 언제든지 재처리할 수 있다
  • 풀 모델을 사용한다. → 소비자가 카프카 브로커에서 메시지를 읽어 가는 방식

래빗MQ 고려 사항

  • 클러스트를 통해 처리량을 높일 수 있다 → 카픜카보다 더 많은 자원을 필요로 한다.
  • 메모리에만 메시지를 보관하는 큐 설정을 사용하면 장애 상황 시 메시지가 유실될 수 있다
  • 메시지는 큐에 등록된 순서대로 소비자에 전송된다
  • 메시지가 소비자에 전달됐는지 확인하는 기능을 제공
  • 푸시 모델을 사용 → 래빗MQ 브로커가 소비자에 메시지를 전송, 소비자 성능이 느려지면 큐에 과부하가 걸려 전반적으로 성능 저하 발생
  • 다재 다능하다 → AMQP, STOMP 등 여러 프로토콜을 지원하고, 게시/구독 패턴뿐만 아니라 요청/응답, 점대점 패턴을 지원 → 우선 순위를 지정해서 처리 순서를 변경할 수도 있다.

레디스 pub/sub 고려 사항

  • 메모리를 사용하므로 지연 시간이 짧고, 래빗MQ 대비 처리량이 높다
  • 구독자가 없으면 메시지가 유실
  • 기본적으로 영구 메시지를 지원하지 않는다
  • 모델이 단순해서 사용하기 쉽다

결론 : 메시지 유실 상관없다면 레디스, 카프카나 래빗MQ에 비해 사용법이 간단, 적은 장비로 높은 성능을 낼 수 있다.
트래픽이 대량으로 발생한다면 카프카 고려,
트래픽 규모가 크지 않고 메시지를 정확하게 순서대로 소비자에 전달해야 하거나 AMQP나 STOM{P 프로토콜로 연동해야 한다면 래빗MQ 고려

메시지 생성 측 고려 사항

메시지를 생성할 때 고려할 점

무시한다.

  • 가장 쉬운 방법
  • 메시지는 유실된다.

재시도 한다.

  • 일시적인 네트워크 불안정과 같은 오류
  • 재시도하는 과정에서 중복된 메시지가 전송될 수 있다.
  • 메시지마다 고유 식별자를 사용하면 중복 메시지 여부 판단 유용

실패 로그를 남긴다

  • 로그는 나중에 후처리를 하는 데 사용
  • 후처리에 필요한 데이터를 남긴다 → DB나 파일

메시지 소비 측 고려 사항

메시지 생성자가 같은 데이터를 가진 메시지를 메시징 시스템에 두 번 전송

  • 메시지에 고유한 ID를 부여해서 이미 처리했는지 여부를 추적한다.
  • 이미 처리한 메시지는 다시 처리하지 않고 무시할 수 있다.

소비자가 메시지를 처리하는 과정에서 오류가 발생해서 메시지 재수신

  • 소비자는 메시지 처리에 실패했다고 생각하고 메시징 시스템으로부터 같은 메시지를 다시 수신해서 재시도할 수 있다.
  • API가 멱등성을 가지면 동일 요청을 여러 번 해도 결과가 바뀌지 않음

메시지 종류: 이벤트와 커맨드

예시

이벤트커맨드

| 1. 주문함
2. 로그인에 실패함
3. 상품 정보를 조회함
4. 배송을 완료함 | 1. 포인트 지급하기
2. 로그인 차단하기
3. 배송 완료 문자 발송하기 |

이벤트

  • 어떤 일이 발생했음을 알려주는 메시지
  • 상태 변경과 관련
  • 정해진 수신자가 없다.
  • 소비자 확장에 적합

커맨드

  • 무엇가를 요청하는 메시지
  • 소비자는 메시지로 요구한 기능을 실행

트랜잭션 아웃박스 패턴

메시지 데이터를 DB에 보관하는 방식

실제 없무 로직에 필요한 DB 변경 작업을 수행

  • DB 트랜잭션 범위에서 아웃박스 테이블에 메시지 데이터를 추가하므로 메시지 데이터가 유실되지 않음
  • 트랜잭션을 롤백하면 메시지 데이터도 함꼐 롤백 → 잘못된 메시지 데이터가 전송될 일 없음

메시지 데이터를 아웃박스 테이블에 추가

  • 3가지 상태(발송 대기, 발송 완료, 발송 실패)를 갖는다
  • 쉽게 모니터링할 수 있다

아웃박스 테이블 구조

컬럼명타입설명
idBIGINT (PK)아웃박스 레코드의 고유 식별자
message_typeVARCHAR(100)메시지 타입
message_idVARCHAR(100)메시지 고유 id
payloadclob메시지 데이터
statusVARCHAR(20)처리 상태 (waiting, done, failed 등)
fail_countint실패 횟수
created_atTIMESTAMP메시지 발생 시간
processed_atTIMESTAMP메시지 처리 시간
falled_atTIMESTAMP마지막 실패 시간

배치 전송

데이터를 비동기로 연동하는 가장 전통적인 방법

실행 과정

  1. DB에서 전송할 데이터를 조회
  2. 조회한 결과를 파일로 기록
  3. 파일을 연동 시스템에 전송

사용하는 파일 형식

값1(구분자)값2(구분자)값3(구분자)

  • 각 값을 특정 문자를 이용해 구분하는 방식
  • 값의 위치에 따라 의미가 결정
  • 구현 간단
  • 파싱 속도도 빨라서 널리 사용

이름1=값1 이름2=값2 이름3=값4

  • 이름과 값 한쌍으로 묶음
  • 각 쌍은 공백 문자로 구분, 값에 공백이 포함될 수 있다면 탭이나 ^같은 구분자 사용하기도 함
  • 위치에 관계없이 어떤 값인지 알 수 있다
  • 값/구분자보다 데이터 크기가 더 커짐

json문자열

  • 대부분의 프로그래밍 언어는 json 변환 기능을 제공
  • 데이터 크기가 커진다.

재처리 기능 만들기

어떤 이유에서든 전송에 실패하면 일정 시간 후에 재전송하는 기능을 구현해 두어야 한다

  • 배치가 성공했는지 확인하고 실패했다면 재실행하는 방식
  • 한두 번 정도만 재시도해도 수작업으로 재처리하는 번거로움을 상당히 줄일 수 있음

CDC

변경된 데이터를 추적하고 판별해서 변경된 데이터로 작업을 수행할 수 있도록 하는 소프트웨어 설계 패턴

  • DB는 변경된 데이터를 CDC 처리기에 전송
  • 커밋된 데이터만 변경된 순서에 맞게 전달
  • CDC 처리기에는 롤백된 데이터가 전달되지 않는다
  • 잘못된 순서로 데이터가 전달되는 일도 없다

CDC 처리기 대상 시스템 변경 데이터 전파 형태

  1. 변경 데이터를 그대로 대상 시스템에 전파
    1. 1대1의 관계를 가질 때 적합
  2. 변경 데이터를 가공/변환해서 대상 시스템에 전파
  3. 목적에 따라 CDC 처리기
  4. 메시징 시스템에 데이터를 전파하면 여러 시스템에 변경된 데이터를 전달할 수 있어 확장에 유리

CDC와 데이터 위치

변경 데이터를 어디까지 처리했는지 기록해야 한다.

  • 로그 파일에서의 위치를 기록해야 CDC 처리기를 재시작할 때 마지막으로 조회한 로그부터 읽어올 수 있음
  • 위치를 기록하지 않으면 마지막 로그 데이터부터 읽어와야 하는데, 재시작하는 시간 동안 발생한 변경 데이터를 놓치게 된다.

CDC가 유용할 때

시스템이 복잡해서 연동 코드를 넣기 부담스러울 때 유용하다

  • 코드를 수정하지 않고도 CDC를 사용해 타 시스템에 관련 데이터를 전파할 수 있음
  • 시스템 개발 일정에 주는 영향을 최소화할 수 있음

자료 출처

https://product.kyobobook.co.kr/detail/S000216376461

profile
코딩하는 대학생

0개의 댓글