[RabbitMQ] JMS 먼저 정리하고...
JMS(Java Message Service)란?
메시징 시스템은 응용프로그램 간에 비동기적으로 메시지를 교환할 수 있는 방법을 제공해주며 구현을 위해서는 MOM(Message Oriented Middleware)가 필요하다.
MOM은 메시지를 전달할 수 있으며, 메시지 전달을 비동기적으로 처리할 수 있는 특징이 있는 시스템이다.
JMS는 각 회사에서 만든 MOM 시스템의 공통적인 부분을 표준화함으로써 공통된 API를 사용할 수 있게 해준다.
<그림출처: https://today.java.net/pub/a/today/2005/06/03/loose.html>
메시지 시스템의 MOM은 아키텍처를 의미하고 이 표준을 구현한 환경이 필요하다.
자바에서는 메시지 시스템을 구현할 수 있는 환경을 제공하는 API를 JMS(Java Message Service)라고 하고
MOM은 J2EE, 웹로직 등 EJB서버에서 메모리 영역을 제공한다.
메시징 서비스의 종류
(1) P2P(Peer to Peer)
P2P Model 은 Sender가 보낸 메세지를 하나의 Receiver만 받도록 되어 있다.
Sender가 메세지를 보내면 그 내용은 Queue에 저장이된다. JMS Queue는 그 메세지를 저장하고 있다가. Receiver가 그 Queue에 접속이 되면, 그 내용을 Receiver에게 전달한다.
하나의 Queue에는 여러개의 Sender나, 여러개의 Receiver가 붙을 수 있지만, 하나의 메세지는 절대로 하나의 Receiver에만 전달된다.
[출처] JMS API의 이해|작성자 투란도트
<그림출처: http://www.developerworkspace.com/Java/jms/tutorials/>
(2) Pub/sub
Publish/Subscribe Messaging Model은 하나의 메세지를 여러 Recevier가 같이 받을 수 있다.
Sender가 보낸 메세지는 JMS의 Topic이라는 곳에 저장이 되며, 이 Topic을 Listening하고 있는 모든 recevier에게 전달이 된다.
[출처] JMS API의 이해|작성자 투란도트
<그림출처: http://www.developerworkspace.com/Java/jms/tutorials/>
응용프로그램의 구성요소
JMS 명세에 따르면 JMS를 이용하는 응용 프로그램은 다음과 같은 요소로 구성되며, 각각의 요소는 API에서 제공하는 객체가 될 수도 있고 어떤 부분은 응용 프로그램일 수 도 있다.
(1) 운영도구(Administrative Tool)
MOM을 이용해서 P2P나 pub/sub 방식으로 메시지를 전달하고 메시지를 전달 받기 위한 API는 JMS에서 제공하지만 MOM 자체를 관리하기 위한 방법은 JMS에서 제공하지 않는다. 그렇기 때문에 MOM을 운영하려면 MOM에서 제공하는 운영도구를 사용해야 한다. MOM을 운영한다는 것은 목적지(destination)를 설정하기 위한 것이고, 목적지는 P2P나 pub/sub 방식으로 메시지를 주고 받을 때 사용하기 위한 저장 공간이라고 생각하면 된다.
(2) 커넥션 팩토리(Connection Factories)
커넥션과 관련된 정보를 통해서 커넥션을 생성할 수 있는 인터페이스를 말한다. 클라이언트는 JNDI를 이용하여 커넥션 팩토리(Connection Factory)를 검색한 후 JMS 커넥션을 만들게 되는데, P2P 방식일 경우에는 javax.jms.QueueConnectionFactory를 사용하며, pub/sub 방식일 경우에는 javax.jms.TopicConnectionFactory 를 사용한다.
(3) 목적지(Destinations)
메시지를 MOM 에게 전송하면 목적지에 메시지가 전달된다.
(4) 커넥션(Connection)
커넥션은 JMS 서비스 제공자와 클라이언트 간의 TCP/IP 커넥션을 의미하며, 세션을 생성하기 위해 사용된다.
(5) 세션(Session)
세션은 클라이언트와 JMS서버 간에 메시지를 생성하고 소비하기 위한 단일 스레드 환경이다.
P2P방식에서는 javax.jms.QueueSession을 pub/sub방식에서는 javax.jms.TopicSession을 사용한다.
(6) 메시지 생산자(Message Producer)
클라이언트에서 메시지를 JMS서버에 전달하려면 메시지 생산자(Message Producer)을 이용해야 한다.
메시지 생산자는 세션을 통해서 만들어 질 수 있다. P2P 방식일 경우에는 QueueSender를, pub/sub 방식일 경우에는 TopicPublisher를 통해서 생성한다.
(7) 메시지 소비자(Message consumer)
클라이언트는 JMS 서버로부터 메시지를 받아들이기 위해서 메시지 소비자(Message Consumer)를 이용해야 한다. 메시지 소비자는 생산자와 마찬가지로 세션을 통해서 만들어질 수 있다. P2P방식을 경우에는 QueueReceiver를, pub/sub방식일 경우에는 TopicSubscriber를 통해서 생성한다.
(8) 메시지(Message)
메시지는 메시지 생산자와 소비자 간에 교환되는 데이터를 말한다. 메시지는 헤더(header), 특성(property), 몸체(body)로 구성되어 있으며 어떠한 데이터라도 몸체에 담겨 전송할 수 있다.
JMS API Programming Model
JMS 프로바이드 환경을 설정하고나면, JMS 클라이언트를 구축해야 한다.
JMS 어플리케이션은 다음 순서로 처리된다.
1- Access JMS provider
2- Create administered Connection: connection factories and destinations
3- Create Connection
4- Create Session
5- Create Producer and consumer
6- Create Message
7- Send and receive Messages
<그림출처: http://www.developerworkspace.com/Java/jms/tutorials/>
Context ctx = new InitialContext(); ( if lookup in the current vendor) Or Hashtable env = new Hashtable(); String initialContextFactory="com.evermind.server.rmi.RMIInitialContextFactory"; String securityPrincipal="userName"; String credentials="userPassword"; env.put( Context.INITIAL_CONTEXT_FACTORY,initialContextFactory ); env.put( Context.SECURITY_PRINCIPAL, securityPrincipal ); env.put( Context.SECURITY_CREDENTIALS, credentials); ctx = new InitialContext(env);
(2) Connection factories and Destinations 생성
1. Connection Factory: ConnectionFactory는 JMS의 Connection을 얻어오는 역할을 한다. JMS 서버로의 Connection은 각 JMS 서버 Vendor에 dependent 되기 때문에, 이를 통일하기 위해서 ConnectionFactory라는 Object를 통해서 JMS 서버로의 Connection을 얻어오게 되어 있다. 이 Connection Factory는 JMS Server의 JNDI Tree로 부터 얻어오게된다.
[출처] JMS API의 이해|작성자 투란도트
QueueConnectionFactory queueConnectionFactory = (QueueConnectionFactory) ctx.lookup("jms/QueueConnectionFactory"); TopicConnectionFactory topicConnectionFactory = (TopicConnectionFactory) ctx.lookup("jms/TopicConnectionFactory");
Queue나 Topic은 JMS 서버에 의해서 관리 되기 때문에, Connection Factory Object와 마찬가지로 JMS 서버의 JNDI로 부터 얻어오게된다.
[출처] JMS API의 이해|작성자 투란도트
Queue myQueue = (Queue) ctx.lookup("jms/MyQueue"); Topic myTopic = (Topic) ctx.lookup("jms/MyTopic");
Connection은 말그대로, Application에서 JMS Server의 Connection을 나타낸다.
[출처] JMS API의 이해|작성자 투란도트
QueueConnection queueConnection = queueConnectionFactory.createQueueConnection(); TopicConnection topicConnection = topicConnectionFactory.createTopicConnection();Note: When an application completes, you need to close any connections that you have created. Failure to close a connection can cause resources not to be released by the JMS provider. queueConnection.close(); topicConnection.close();
세션은 메시지 생성과 소비를 위한 싱글 스레드 컨텍스트이다.
Session이라는 개념이 생긴것은 예를 들어, 하나의 JMS서버에 클라이언트가 접속했는데, 하나의 Queue가 아닌 동시에 여러개의 Queue에 통신하고자할때를 위해서 Session이라는 개념이 도입되었다. 쉽게 생각하면 Connection은 JMS server로의 물리적인 연결이라 생각하면 되고, Session은 그 물리적 연결내에 있는 논리적인 연결이라고 생각하면 된다.
[출처] JMS API의 이해|작성자 투란도트
QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); TopicSession topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
Session에 앞에 인자는 boolean값으로, 해당 JMS Session을 JTA Transaction에 포함시킬지 여부를 나타낸다. 뒤에는 메세지의 대한 Ack 방법을 정의한다. (Transaction에 포함시키는 경우-true일경우, 뒤에 ACK방법에 대한 인자는 무시된다.)
[출처] JMS API의 이해|작성자 투란도트
[출처] JMS API의 이해|작성자 투란도트
QueueSender queueSender = queueSession.createSender(myQueue); TopicPublisher topicPublisher = topicSession.createPublisher(myTopic);Consumer:
QueueReceiver queueReceiver = queueSession.createReceiver(myQueue); TopicSubscriber topicSubscriber = topicSession.createSubscriber(myTopic);
(6) Message 생성
실질적으로 보내지는 메세지를 나타낸다. session 객체로 부터 생성한다.
[출처] JMS API의 이해|작성자 투란도트
JMS API는 다섯가지의 메지시 타입을 정의할 수 있다.
[ TextMesssage, MapMessage, BytesMessage, StreamMessage, ObjectMessage, Message]
TextMessage message = queueSession.createTextMessage(); message.setText(msg_text); // msg_text is a String
(7) 메시지 송수신
Sending: QueueSender 와 TopicPublisher 를 사용하여 메시지를 전송할 수 있다.
queueSender.send(message); topicPublisher.publish(message);
Receiving: QueueReceiver 와 TopicSubscriber 을 이용하여 메소드가 호출되고 난 뒤 아무때나 메시지를 수신할 수 있다.
queueConnection.start(); // To consume a message synchronously you can use receive method as following: Message m = queueReceiver.receive(); or Message m = queueReceiver.receive(1000); // time out after a second if (m instanceof TextMessage) { TextMessage message = (TextMessage) m; System.out.println("Reading message: " + message.getText()); } else { // Handle error }
QueueListener queueListener = new queueListener(); queueReceiver.setMessageListener(queueListener);