Java & Spring

Message Broker를 사용하는 이유

ju_young 2024. 5. 21. 10:49
728x90

SSE(Server-Sent Events)를 사용하여 서버에서 클라이언트로 알림 메시지를 전송할 경우 아래와 같이 단순하게 구현할 수 있다.


이때 서버는 저장해둔 SseEmitter를 사용하여 클라이언트에게 메시지를 전송한다.

 

하지만 이후 서버가 추가되어 여러 대가 될 경우 다음과 같은 문제가 발생한다.

클라이언트가 4개, 서버가 3대일 경우 다음과 같은 순서로 동작한다고 해보자.

  1. Client A는 Server A에게 SSE 연결을 요청
  2. Client A는 댓글 작성과 같은 이벤트 발생
  3. Server B에서 Client A의 이벤트를 처리
  4. Server B에서 Client A로 알림 메시지를 전송하기위해 SseEmitter를 조회

Client A에 대한 SseEmitter는 Server A에 저장했지만 이벤트를 Server B에서 처리하게될 경우 저장한 SseEmitter를 찾지못해 메시지를 전송하지 못하게된다. 또한 메시지가 유실되어버린다.

 

이러한 문제를 Message Broker를 사용하여 해결해줄 수 있다.

 

  1. Server A에서 Client A의 이벤트를 처리하고 Message Broker로 publish
  2. 모든 서버는 Message Broker에서 메시지를 받아서 처리하기위해 Listener를 적용한다. 따라서 Message Broker는 모든 서버로 broadcast 한다.
  3. Client A의 SseEmitter를 가지고 있는 Server B는 Client A로 메시지 전송

또한 Message Broker는 메시지를 받아 큐에 저장해두고 서버에 문제가 발생하더라도 유지하고 있다. 이후 다시 서버가 정상적으로 돌아오면 유지해둔 메시지를 받을 수 있다. 즉, 메시지 전달이 보장된다.

exchange 및 queue를 잃지 않게 하기위한 설정

durable=true, auto-delete=false로 설정해주면 된다.

  • durable: 브로커가 restart되더라도 살아남음
  • auto-delete: 마지막 consumer가 unsubscribe했을 때 삭제

이렇게 설정해주면 exchange와 queue는 서버가 재시작되거나 클라이언트가 더 이상 없더라도 계속 존재할 수 있게된다.

거부된 메시지들을 읽지 않게 하기위한 설정

전송에 실패한 메시지를 잡아내기위해 dead-letter-queue를 설정해줄 수 있다.

 

dead-letter-queuedead-letter-exchange와 동반되며 거부된 메시지들을 처리하기위한 방법으로 사용된다.

보통 exchange는 접미사로 .dxl로 설정하고 queue는 .dlq로 설정한다. (이때 둘 다 durable로 설정)

안전하게 메시지 Consume

우선 auto-ack를 false로 설정해주어야한다.

 

auto-ack가 true로 설정되면 다음과 같이 동작한다.

  1. queue로 메시지 publish
  2. consumer로 메시지 전달
  3. consumer는 queue에게 ack 리턴
  4. queue에서 메시지 삭제

이때 실제로 consumer에서 메시지를 처리하는 시간이 더 길기 때문에 queue에서의 메시지가 먼저 삭제된다. 따라서 consumer에서 문제가 생겼을 경우 메시지 유실이 발생하게된다.

 

그리고 메시지 전달에 실패했을 경우는 DLQ로 가도록 해야한다.

안전하게 Publish

메시지는 메모리에 저장되며 재실행되거나 라우팅할 수 없는 경우 유실될 수 있다.

  1. mandatory를 true로 설정해주면 queue에 메시지가 추가되었을 경우 success를 반환하고 라우팅되지 않을 경우 error를 반환한다.
  2. persistent를 true로 설정해주면 메시지가 disk에 저장된다. (동시에 RAM에 저장됨)
  3. open channel에서 waitForConfirmsOrDie을 호출하여 메시지가 정상적으로 확인되었는지 알 수 있다.

또한 lazy queue mode를 사용하여 가능한 빨리 disk로 옮기도록 할 수 있다. 이렇게하면 더 적게 메시지를 RAM에 유지할 수 있게 만든다. (java에서 args에 x-queue-mode=lazy를 추가해주면 된다.)

 

[reference]

728x90