소프트웨어 구성요소(서비스)간의 디커플링은 소프트웨어 설계의 가장 중요한 부분중 하나이다. 그 방법중 하나는 구성요소(서비스)간 비동기식 통신을 제공하는 메시징 시스템을 사용하는 것이다.
RabbitMQ가 그중 하나란 얘기다.
먼저 메시징이 어떻게 동작하는 살펴보자
메시징 시스템과 상호작용하는 어플리케이션에는 producers, consumers 두가지가 있다. producers는 broker에게 메시지를 발송하고 consumers는 broker로부터 메시지를 받는다. 보통 program(구성요소)은 다른 머신(서버라 생각하는게 더 쉬울거 같다)에서 동작하고 RabbitMQ는 그들 사이에서 통신 미들웨어 역할을 한다.
RabbitMQ는 여러 프로토콜을 지원하는데 이 튜토리얼에서는 메시징을 위한 개방형 프로토콜인 AMQP 0-9-1과 java로 된 RabbitMQ 클라이언트를 사용한다.
튜토리얼에서는 slf4j라이브러리를 사용하는데 maven, gradle을 사용하는 경우에는 amqp-client를 사용해도 된다.
용어에서 보자면 Producer역할이다.
https://www.rabbitmq.com/tutorials/tutorial-one-java 튜토리얼에 있는 소스이다.
package my.study.rabbitmqstudy.rabbitmp;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbiMQSend {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory();
Connection connection = null;
Channel channel = null;
try{
factory.setHost("localhost");
connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "이 편지는 영국에서 시작되어2";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
} catch (IOException e) {
throw new RuntimeException(e);
} catch (TimeoutException e) {
throw new RuntimeException(e);
}
}
}
QUEUE_NAME으로 queueDeclare메소드를 이용하여 queue를 선언하고 basicPublish로 원하는 큐를 지정하여 메시지를 보낸다.
용어에서 보자면 Consumer역할이다.
package my.study.rabbitmpstudyrecv.rabbitmp;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.concurrent.TimeoutException;
public class RabbitMQRecv {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory();
Connection connection = null;
Channel channel = null;
try{
factory.setHost("localhost");
connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println("Message recieve : "+message);
System.out.println(" 받은시간 : " + LocalDateTime.now());
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});
} catch (IOException e) {
throw new RuntimeException(e);
} catch (TimeoutException e) {
throw new RuntimeException(e);
}
}
}
QUEUE_NAME으로 queueDeclare메소드를 이용하여 queue를 선언한다, producer쪽은 보내기위한 queue를 선택한 것이라면 consumer쪽은 받을 queue를 선택하는 것이다. basicConsume메소드를 통해서 대기를 시작하면 된다.
RabbiMQSend쪽 콘솔이다.

RabbitMQRecv쪽 콘솔이다.

보낸시간과 받은시간을 비교하면 0.01초만에 consumer쪽에서 수신하였다.
이렇게 HTTP통신으로 데이터를 주고 받는게 아닌 message를 통해서 시스템간에 정보를 주고 받는것을 요즘 흔히 말하는 event기반 시스템이라 부르는 거 같다.