메시지 브로커는 데이터(메시지)를 송신자(프로듀서)로부터 수신자(컨슈머)에게 전달하는 중간 매개체 역할
RabbitMQ는 이러한 메시지를 큐(queue)에 저장하고, 필요할 때 적절한 수신자에게 전달
장점
단점
메시지(Message)
프로듀서(Producer)
큐(Queue)
컨슈머(Consumer)
익스체인지(Exchange)
라우팅 키가 정확히 일치하는 큐로 메시지를 전달
예를 들어, 라우팅 키가 error인 메시지는 error라는 바인딩 키를 가진 큐로 전달
2) Topic Exchange
라우팅 키의 패턴을 사용하여 메시지를 라우팅. 패턴에는 와일드카드 * (단어 하나)와 # (0개 이상의 단어)가 사용.
예를 들어, 라우팅 키가 quick.orange.rabbit인 메시지는 바인딩 키가 .orange.인 큐로 전달
3) Fanout Exchange
라우팅 키를 무시하고 교환기에 바인딩된 모든 큐로 메시지를 브로드캐스트
모든 바인딩된 큐로 메시지가 전달
4) Headers Exchange
라우팅 키 대신 메시지의 헤더를 기반으로 메시지를 라우팅
헤더 값과 바인딩된 헤더 값이 일치하는 큐로 메시지를 전달합니다.

RabbitMQ
도커를 사용하여 RabbitMQ를 설치합니다.
**docker run -d --name rabbitmq -p5672:5672 -p 15672:15672 --restart=unless-stopped rabbitmq:management**

localhost:15672에 접속하면 로그인 페이지가 보입니다. guest/guest를 입력하여 접속하면 대시보드를 볼 수 있습니다.


Order Application
start.spring.io 에 접속하여 프로젝트를 생성합니다.

프로젝트를 생성하면 build.gradle의 디펜던시는 아래와 같습니다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-amqp'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.amqp:spring-rabbit-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
application.properties
spring.application.name=order
message.exchange=market
message.queue.product=market.product
message.queue.payment=market.payment
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
OrderApplicationQueueConfig.java
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OrderApplicationQueueConfig {
@Value("${message.exchange}")
private String exchange;
@Value("${message.queue.product}")
private String queueProduct;
@Value("${message.queue.payment}")
private String queuePayment;
@Bean public TopicExchange exchange() { return new TopicExchange(exchange); }
@Bean public Queue queueProduct() { return new Queue(queueProduct); }
@Bean public Queue queuePayment() { return new Queue(queuePayment); }
@Bean public Binding bindingProduct() { return BindingBuilder.bind(queueProduct()).to(exchange()).with(queueProduct); }
@Bean public Binding bindingPayment() { return BindingBuilder.bind(queuePayment()).to(exchange()).with(queuePayment); }
}
OrderController.java
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
@GetMapping("/order/{id}")
public String order(@PathVariable String id) {
orderService.createOrder(id);
return "Order complete";
}
}
OrderService.java
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class OrderService {
@Value("${message.queue.product}")
private String productQueue;
@Value("${message.queue.payment}")
private String paymentQueue;
private final RabbitTemplate rabbitTemplate;
public void createOrder(String orderId) {
rabbitTemplate.convertAndSend(productQueue, orderId);
rabbitTemplate.convertAndSend(paymentQueue, orderId);
}
}
애플리케이션을 런하여 /order/1 로 요청을 보냅니다. 그후 http://localhost:15672로 접속하여 RabbitMQ의 Exchange와 Queue 를 확인 할 수 있습니다.
또한 Queue 에서는 현재 발행된 메시지가 Total 에 쌓여 있는것을 확인 할 수 있습니다.



Queue and Stream 페이지에서 큐 이름을 클릭하여 상세페이지로 이동한 후 스크롤하여 Get Messages섹션으로 가서 Get Message를 클릭하면 현재 큐에 쌓여있는 메시지를 조회 할 수 있습니다.

Payment Consumer 만들기
start.spring.io 에 접속하여 프로젝트를 생성합니다.

프로젝트를 생성하면 build.gradle의 디펜던시는 아래와 같습니다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-amqp'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.amqp:spring-rabbit-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
application.properties
spring.application.name=payment
server.port=8081
message.queue.payment=market.payment
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
PaymentEndpoint.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class PaymentEndpoint {
@Value("${spring.application.name}")
private String appName;
@RabbitListener(queues = "${message.queue.payment}")
public void receiveMessage(String orderId) {
log.info("receive orderId:{}, appName : {}", orderId, appName);
}
}
애플리케이션을 실행하면 receiveMessage 의 로그가 찍히는것을 확인. Order 프로젝트에서 발행한 메시지가 Payment Consumer가 실행되자마자 소모

보면 product는 소모 안되고 payment만 소모된 걸 볼 수 있음

Product Consumer 만들기
start.spring.io 에 접속하여 프로젝트를 생성합니다.

프로젝트를 생성하면 build.gradle의 디펜던시는 아래와 같습니다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-amqp'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.amqp:spring-rabbit-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
application.properties
spring.application.name=product
message.queue.product=market.product
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
ProductEndpoint.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ProductEndpoint {
@Value("${spring.application.name}")
private String appName;
@RabbitListener(queues = "${message.queue.product}")
public void receiveMessage(String orderId) {
log.info("receive orderId:{}, appName : {}", orderId, appName);
}
}
컨슈머가 라운드로빈으로 메시지를 전달 받는것을 확인해가 위해서 Intellij 에서 구성편집에 들어가 두개의 Product를 생성합니다. application.name 옵션을 통해 2를 구분할 수 있도록 합니다.
VM 옵션 추가 기능을 켜야지 뜸.


order에서 요청 할때마다 Product Application이 번갈아 가면서 메시지를 수신 받는것을 확인 할 수 있습니다.

