이번 포스팅에서는 이번 포스팅에서는 메세지 큐(Message Queue) 을 알아보자 에 이어서
실제로 RabbitMQ
을 Spring boot
에 적용해보겠습니다 🔥
Spring 에 RabbitMQ
을 구축하기 앞서 정의와 필요한 개념과 구성 요소를 살펴보겠습니다 👨💻
RabbitMQ
는 AMQP(Advanced Message Queue Protocol)
을 구현한 오픈소스 메세지 브로커입니다.
대중적으로 많이 사용되는 메세지 브로커이며 빠르고 쉽게 구성할 수 있으며 직관적입니다.
1. [RabbitMQ 용어 정리]
Publish
함Producer
로 부터 메세지를 받아 처리하는 주체Producer
로 부터 전달받은 메세지를 어떤 Queue
로 보낼지 결정하는 장소등록
됨Routing Key(Binding Key)
존재Consumer
가 메세지를 소비하기 전까지 메세지를 보관하는 장소Queue
관계를 정의 즉, Binding 에 따라서 Exchange 에 발행된 메세지가 어떤 Queue 로 갈지 결정된다.Routing Key(Binding Key)
을 수단으로 이루어짐2. [Binding 전략]
앞서 살펴본바와 같이 Binding
이란 Producer
로 부터 메세지를 받은 Exchange
가 어떤 Queue
로 메세지를 전달할지에 대한 방식이였습니다.
-> Exchange
에 Queue
을 등록하고 Queue
에 보관되어 있는 메세지를 해당 Queue
을 바라보는 Consumer
가 소비하는 방식
여기에는 4가지 전략이 있습니다❗️
Routing Key
와 정확히 일치하는 Binding
된 Queue
로 RoutingDirect Exchange
에 따르면, 메세지에 부여된 Routing Key
와 동일한 Key 로 Binding
된 Queue
에만 전송합니다.
해당 이미지는 Direct Exchange
방식을 도식화한 이미지인데, rabbit key 을 가진 메세지는 Exchange
을 거쳐 rabbit 으로 binding 된 Queue
로 전송됩니다 👨💻
Binding
된 모든 Queue
에 메세지를 RoutingFanout Exchange
전략은, Routing Key
에 상관 없이 Exchange
에 등록된 모든 Queue
에 메세지를 전송합니다.
Routing Patten
이 일치하는 Queue
로 RoutingTopic Routing
은 Routing Key
의 패턴을 이용해 메세지를 Routing 합니다.
여러 Consumer
에서 메세지 형식에 따라 선택적으로 수신해야 하는 경우 등에 사용됩니다.
위의 이미지는 Topic Routing
을 도식화한 것인데, animal.rabbit 이라는 Routing key 로 Exchange
에 메세지가 발행되었습니다.
그러면 이와 일치하는 패턴을 가지는 Routing Key
로 Binding 된 Queue
로 메세지가 전달됩니다.
animal.rabbit 에 animal 과 # 은 일치하는 패턴이기에 정상적으로 전달됩니다❗️
Header
속성을 통한 RoutingHeaders Exchange
는 앞서 살펴보았던 Topic Exchange
와 비슷하지만 Header 을 이용하는 차이점이 있습니다.
Producer
에서 정의된 Header 에 Key-Value 쌍과 Consumer
에서 정의된 Argument 의 Key-Value 쌍이 일치하면 Binding 됩니다.
Key | Value | 설명 |
---|---|---|
x-match | all | header 의 모든 key-value 쌍 값과 argument의 모든 key-value 쌍이 일치할때만 Binding |
x-match | any | header 의 key-value 쌍 값과 argument의 하나의 key-value 쌍과 일치하더라도 Binding |
3. [RabbitMQ 구성 요소의 세부 속성]
Exchange
이름Binding
전략(앞서 살펴본 4가지 전략)Exchange
메세지가 저장되어 남아있음Exchange
메세지가 모두 사라짐Queue
연결이 해제되면 Exchange
삭제됨이번 실습에서는 4가지 Binding
전략 중 일반적인 전략인 Direct Exchange
을 사용해보겠습니다.
자 그럼 이제부터 실제로 Spring 애플리케이션과 함께 RabbitMQ
을 구축해보겠습니다❗️
implementation 'org.springframework.boot:spring-boot-starter-amqp'
spring:
rabbitmq:
host: localhost // RabbitMQ host ip
port: 5672 // RabbitMQ port
username : guest // RabbitMQ 웹 관리 콘솔 아이디
password: guest // RabbitMQ 웹 관리 콘솔 비밀번호
rabbitmq:
queue:
name: sample-queue // 사용할 queue 이름
exchange:
name: sample-exchange // 사용할 exchange 이름
routing:
key : key
docker run -d --name rabbitmq -p
5672:5672 -p 15672:15672 --restart=unless-stopped rabbitmq:management
포트 5672
는 RabbitMQ 브로커(Broker) 연결, 포트 15672
는 RabbitMQ 웹 관리 콘솔에 사용됩니다.
결국, RabbitMQ 또한 하나의 서버상에서 구동되는 시스템이기 때문에 이와 같이 Docker 을 이용해서 구축해줍니다.
해당 이미지는 실제RabbitMQ
웹 관리 콘솔창 이미지입니다.
콘솔창에서 RabbitMQ
에 대한 다양한 정보를 확인할 수 있으며 조작이 가능합니다 👨💻
RabbitMQ
의 정상적인 동작을 위해 설정 클래스를 만듭니다.
SpringBoot
어플리케이션에 RabbitMQ
을 구축하기 위해서는 필요한 설정값이 있습니다.
Host
,Port
,QueueName
,ExchangeName
등 다양한 설정값은 위에서 살펴본바와 같이 application.yml
에 설정하면 되는데, 이번 실습에서 spring.rabbitmq
의 prefix 을 가지는 값들을 RabbitMqProperties
클래스 필드로 바인딩 한 후 사용하려고 합니다 ❗️
@ConfigurationProperties
어노테이션을 사용하는 방법에 익숙하지 않다면 [Spring] Spring 설정 파일에 있는 설정 값을 애플리케이션에서 활용하는 방법 (@Value,@ConfigurationProperties) 을 참고해주세요.
@ConfigurationProperties(prefix = "spring.rabbitmq")
@ConstructorBinding
@AllArgsConstructor
@Getter
public class RabbitMqProperties {
private String host;
private int port;
private String username;
private String password;
}
@ConfigurationPropertiesScan // class path 존재하는 모든 ConfigurationProperties Scan(필수❗️)
@SpringBootApplication
public class RedisApplication {
public static void main(String[] args) {
SpringApplication.run(RedisApplication.class, args);
}
}
@RequiredArgsConstructor
@Configuration
public class RabbitMqConfig {
private final RabbitMqProperties rabbitMqProperties;
@Value("${rabbitmq.queue.name}")
private String queueName;
@Value("${rabbitmq.exchange.name}")
private String exchangeName;
@Value("${rabbitmq.routing.key}")
private String routingKey;
// org.springframework.amqp.core.Queue
@Bean
public Queue queue() {
return new Queue(queueName);
}
/**
* 지정된 Exchange 이름으로 Direct Exchange Bean 을 생성
*/
@Bean
public DirectExchange directExchange() {
return new DirectExchange(exchangeName);
}
/**
* 주어진 Queue 와 Exchange 을 Binding 하고 Routing Key 을 이용하여 Binding Bean 생성
* Exchange 에 Queue 을 등록한다고 이해하자
**/
@Bean
public Binding binding(Queue queue, DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(routingKey);
}
/**
* RabbitMQ 연동을 위한 ConnectionFactory 빈을 생성하여 반환
**/
@Bean
public CachingConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(rabbitMqProperties.getHost());
connectionFactory.setPort(rabbitMqProperties.getPort());
connectionFactory.setUsername(rabbitMqProperties.getUsername());
connectionFactory.setPassword(rabbitMqProperties.getPassword());
return connectionFactory;
}
/**
* RabbitTemplate
* ConnectionFactory 로 연결 후 실제 작업을 위한 Template
*/
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter());
return rabbitTemplate;
}
/**
* 직렬화(메세지를 JSON 으로 변환하는 Message Converter)
*/
@Bean
public MessageConverter jackson2JsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
RabbitMqConfig
클래스 내부에 각 부분에 대한 설명이 있으니 참고바랍니다.
/**
* Queue 로 메세지를 발핼한 때에는 RabbitTemplate 의 ConvertAndSend 메소드를 사용하고
* Queue 에서 메세지를 구독할때는 @RabbitListener 을 사용
*
**/
@Slf4j
@RequiredArgsConstructor
@Service
public class RabbitMqService {
@Value("${rabbitmq.queue.name}")
private String queueName;
@Value("${rabbitmq.exchange.name}")
private String exchangeName;
@Value("${rabbitmq.routing.key}")
private String routingKey;
private final RabbitTemplate rabbitTemplate;
/**
* 1. Queue 로 메세지를 발행
* 2. Producer 역할 -> Direct Exchange 전략
**/
public void sendMessage(MessageDto messageDto) {
log.info("messagge send: {}",messageDto.toString());
this.rabbitTemplate.convertAndSend(exchangeName,routingKey,messageDto);
}
/**
* 1. Queue 에서 메세지를 구독
**/
@RabbitListener(queues = "${rabbitmq.queue.name}")
public void receiveMessage(MessageDto messageDto) {
log.info("Received Message : {}",messageDto.toString());
}
}
실제로 현재 개발 트랜드에서 RabbitMQ
는 서로 다른 어플리케이션 끼리 메세지를 주고 받을 때 사용되지만, 여기서는 간단한 실습을 목표로 하기에 하나의 어플리케이션에 Producer
와 Consumer
을 구현했습니다.
RabbitMqService
클래스는 sendMessage
와 receiveMessage
메소드로 구성되는데,
전자가 Producer
의 역할을 후자가 Consumer
의 역할을 수행합니다❗️
저희는 application.yml
에 사용할 Queue
,Exchange
그리고 둘을 바인딩 시켜줄 Routing key
를 설정하였습니다.
실제 Producer
에서 메세지를 발행할 때는 RabbitTamplate
의 convertAndSend
메소드를 사용합니다👨💻
이렇게 메세지를 발행하면 Direct Exchange
전략에 따라 주어진 Routing Key
로 바인딩된 Queue
로 메세지가 들어가게 되고, 이는 해당 Queue
을 구독하는 Consumer(어플리케이션)
으로 들어가게 됩니다.
@Slf4j
@RequiredArgsConstructor
@RestController
public class RabbitMqController {
private final RabbitMqService rabbitMqService;
@PostMapping("/send/message")
public ResponseEntity<String> sendMessage(
@RequestBody MessageDto messageDto
) {
this.rabbitMqService.sendMessage(messageDto);
return ResponseEntity.ok("Message sent to RabbitMQ");
}
}
RabbitMqController
로 부터 클라이언트로부터 Http 요청을 받아 간단한 테스트를 진행해보겠습니다 ❗️
정상적으로 RabbitMq
가 잘 동작하는 것을 확인할 수 있습니다 🔥
또한 관리자(Admin) 은 15672
포트에서 돌아가는 RabbitMq
웹 관리 콘솔창에서 이와 관련된 여러가지 정보(통계 등) 을 확인할 수 있습니다 🧑🔧
[RabbitMQ] 기초 개념
[Spring Boot] RabbitMQ 연동하기
RabbitMQ - 레빗엠큐 개념 및 동작방식, 실습