RabbitMQ란?
RabbitMQ는 마이크로서비스 간의 비동기 메시징을 지원하는 메시지 브로커(Message Broker) 이다.
즉, 서비스 간 직접적인 통신 없이 큐(Queue) 를 통해 메시지를 주고받을 수 있게 해준다.
📌 RabbitMQ의 주요 특징
비동기 통신 지원: 서비스가 요청을 보내고 기다릴 필요 없이 메시지를 큐에 저장한 후 다른 서비스가 나중에 처리할 수 있다.
마이크로서비스 간 결합도 감소: 서비스 간의 직접적인 의존성을 줄여 유지보수를 쉽게 만든다.
로드 밸런싱 효과: 여러 개의 서비스가 메시지를 소비(Consume)하여 부하를 분산할 수 있다.
확장성 & 내결함성: 메시지는 큐에 저장되므로, 소비하는 서비스가 다운되더라도 데이터가 유실되지 않고 재처리 가능하다.
RabbitMQ 없이 MSA에서 동기 방식 요청을 처리할 때의 문제점
RabbitMQ가 없다면 마이크로서비스는 동기 방식(HTTP 요청-응답)으로 통신하게 된다. 하지만 이는 여러 가지 문제를 유발할 수 있다.
1️⃣ 직접적인 서비스 호출로 인한 강한 결합
user-service에서other-service를 직접 호출하면, 두 서비스가 강하게 결합됨.other-service가 다운되면user-service도 정상적으로 동작하지 못함.
📌 동기 방식 예제 (RabbitMQ 없이 직접 호출)
@RestController
@RequestMapping("/users")
class UserController(private val otherServiceClient: OtherServiceClient) {
@PostMapping("/register")
fun registerUser(@RequestBody user: UserDto): ResponseEntity<String> {
println("User registered: $user")
// 직접 다른 서비스 호출 (동기 방식)
val result = otherServiceClient.processSomething(user.id)
return ResponseEntity.ok("User registered and other service responded: $result")
}
}
🚨 문제점
other-service가 응답할 때까지user-service가 대기해야 함 (비효율적)other-service가 다운되면user-service도 정상 동작하지 않음- 요청이 많아질수록 서버 부하 증가
🚨 그 외 방식의 문제점
- 서비스 간 HTTP REST API 호출 (동기 방식)
- gRPC 사용 (고성능이지만 여전히 동기)
- 데이터베이스 이벤트 소싱 (비동기지만 실시간 아님)
RabbitMQ를 활용한 비동기 요청 처리 (느슨한 결합)
RabbitMQ를 활용하면 서비스 간 직접적인 호출 없이 메시지를 큐에 전달하고, 필요한 서비스가 해당 메시지를 소비(Consume)할 수 있다.
RabbitMQ를 사용한 흐름
1️⃣ user-service에서 회원가입 요청을 받아 데이터베이스에 저장한 후, RabbitMQ에 메시지를 발행(Publish) 한다.
2️⃣ other-service가 RabbitMQ에서 메시지를 소비(Consume) 하여 이메일 발송 등 후처리를 수행한다.
RabbitMQ를 사용한 코드 예제
1️⃣ 회원가입 요청을 처리하고 RabbitMQ에 메시지 발행
@Service
class UserService(private val rabbitTemplate: RabbitTemplate) {
fun registerUser(user: UserDto) {
// 1️⃣ 회원 정보 저장
println("User registered: $user")
// 2️⃣ 회원가입 완료 메시지를 RabbitMQ에 발행
rabbitTemplate.convertAndSend("user.exchange", "user.created", user)
println("User registration event sent to RabbitMQ")
}
}
💡 convertAndSend(교환기, 라우팅 키, 메시지) 를 사용하여 user.exchange로 메시지를 보냄.
2️⃣ 다른 서비스에서 메시지를 소비(Consume)하여 후처리 실행
@Service
class UserEventListener {
@RabbitListener(queues = ["user.queue"])
fun handleUserCreatedEvent(user: UserDto) {
println("Received user created event: $user")
sendWelcomeEmail(user.email, "Welcome to our service, ${user.name}!")
}
private fun sendEmail(email: String, message: String) {
println("Sending email to $email: $message")
}
}
💡 @RabbitListener(queues = ["user.queue"]) 를 사용하여 user.queue에 쌓인 메시지를 가져와 실행한다.
RabbitMQ 설정 및 실행 방법
📌 docker-compose.yml을 사용하여 RabbitMQ 실행
version: '3.8'
services:
rabbitmq:
image: rabbitmq:3-management
container_name: rabbitmq
ports:
- "5672:5672" # AMQP 포트
- "15672:15672" # 관리 콘솔 포트
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
📌 RabbitMQ 실행 명령어
docker-compose up -d
📌 RabbitMQ 웹 관리 콘솔 접속
RabbitMQ 관리 콘솔에서 메시지 상태를 확인할 수 있음.
➡️ http://localhost:15672 접속 후, 기본 계정(guest/guest)으로 로그인.
결론
RabbitMQ를 활용하면 마이크로서비스 간 비동기 통신이 가능해짐
서비스 간 결합도가 낮아지고, 확장성이 높아짐
요청이 많아질 경우, 여러 개의 서비스가 메시지를 분산 처리하여 성능이 향상됨
RabbitMQ가 없으면 각 서비스가 직접 호출해야 하므로 서비스 간 강한 결합이 발생하여 장애에 취약함
💡 즉, MSA에서 RabbitMQ는 필수적인 역할을 함! 🚀