프로세스와는 무관한 작업으로 인한 지연 현상이 발생하여 비동기 방식 처리를 고민하게되었다.


로그 히스토리를 DB에 기록하는 행위들인데 이 부분들에서 INSERT 시 지연이 발생하여 파일로 쌓았다가 긁어가야 하나.. 어떻게해야 하나 고민 하던 중 WAS 에서 제공하는 JMS 를 사용할까 하다가 ActiveMQ, RabbitMQ 를 셈플링 해보았다.


선택은 Cluster, Mirroring 설정이 간편한 RabbitMQ!!


[Cluster] https://www.rabbitmq.com/clustering.html

[Mirroring] https://www.rabbitmq.com/ha.html


그대로 따라 하기만 하면 잘 된다.


[설치]

esl-erlang_17.5-1~centos~6_amd64.rpm

rabbitmq-server-3.5.1-1.noarch.rpm


우선 서버당 /var/lib/rabbitmq/.erlang.cookie 값을 동일 하게 일치 시킨다.


하지만 난 하나의 물리 서버에 MQ서버 NODE 두개씩 올려보았다.


[rabbitmq@dev01]

   - mq11 - /usr/local/mq/domain/mq11

   - mq12 - /usr/local/mq/domain/mq12

[rabbitmq@dev02]

   - mq21 - /usr/local/mq/domain/mq21

   - mq22 - /usr/local/mq/domain/mq21



관리를 편리하게 하기 위해서 NODE 기반 명령어 스크립트를 만들어 둔다.

env.sh 에서 RabbitMQ 포트 번호 및 웹 관리 포트번호를 설정 하게 한다.


각각 디렉토리에 들어가서 각각 start.sh 를 실하면 알아서 뜬다.


env.sh - 기본 동적 설정

#!/bin/sh

NODE_PORT=각각 다르게 포트번호
NODE_MANAGEMENT_PORT=각각 다르게 포트번호(웹 관리 포트)
NODE_NAME=각각 NODE 이름


start.sh - MQ NODE 시작

#!/bin/sh

. ./env.sh


export RABBITMQ_NODE_PORT=$NODE_PORT 
export RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,$NODE_MANAGEMENT_PORT}]"
export RABBITMQ_NODENAME=$NODE_NAME
export RABBITMQ_MNESIA_DIR=/usr/local/mq/domains/$NODE_NAME/mnesia
export RABBITMQ_LOG_BASE=/usr/local/mq/domains/$NODE_NAME/logs
export RABBITMQ_CONFIG_FILE=/usr/local/mq/domains/$NODE_NAME/rabbitmq
/usr/sbin/rabbitmq-server -detached

echo "============================================================="
echo "   RabbitMQ Server 3.5.1-1.noarch           ddakker@gmail.com"
echo "-------------------------------------------------------------"
echo "RABBITMQ_NODENAME=$RABBITMQ_NODENAME"
echo "RABBITMQ_NODE_PORT=$RABBITMQ_NODE_PORT"
echo "NODE_MANAGEMENT_PORT=$NODE_MANAGEMENT_PORT"
echo "RABBITMQ_MNESIA_DIR=$RABBITMQ_MNESIA_DIR"
echo "RABBITMQ_LOG_BASE=$RABBITMQ_LOG_BASE"
echo "RABBITMQ_CONFIG_FILE=$RABBITMQ_CONFIG_FILE.config"
echo "============================================================="


stop.sh - MQ NODE 중지

#!/bin/sh

. ./env.sh

rabbitmqctl -n $NODE_NAME stop


app_start.sh - MQ NODE 관리앱 시작

#!/bin/sh

. ./env.sh

rabbitmqctl -n $NODE_NAME start_app


app_stop.sh - MQ NODE 관리앱 중지

#!/bin/sh

. ./env.sh

rabbitmqctl -n $NODE_NAME stop_app


ctl.sh - rabbitmqctl NODE 옵션 지정한 명령어

#!/bin/sh

. ./env.sh

echo "rabbitmqctl -n $NODE_NAME $1 $2 $3"
rabbitmqctl -n $NODE_NAME $1 $2 $3


status.sh - 상태 정보

#!/bin/sh

. ./env.sh

rabbitmqctl -n $NODE_NAME status
rabbitmqctl -n $NODE_NAME cluster_status


rabbitmq.config - 기본 설정 파일

[
   {mnesia, [{dump_log_write_threshold, 1000}]},
   {rabbit, [
        {log_levels, [{connection, info}]}
   ]},
   {rabbitmq_management, [
       {redirect_old_port, false}
   ]}
].


[NODE 별로 기동]

[rabbitmq@dev01 mq11]$ ./start.sh

[rabbitmq@dev01 mq12]$ ./start.sh

[rabbitmq@dev02 mq21]$ ./start.sh

[rabbitmq@dev02 mq22]$ ./start.sh


[Clustering] - mq11 을 기준으로 한다.(mq11 에서는 안 해도 됨)

[rabbitmq@dev01 mq12]$ ./app_stop.sh

[rabbitmq@dev01 mq12]$ ./ctl.sh join_cluster mq11@dev01

[rabbitmq@dev01 mq12]$ ./app_start.sh



[rabbitmq@dev02 mq21]$ ./app_stop.sh

[rabbitmq@dev02 mq21]$ ./ctl.sh join_cluster mq11@dev01

[rabbitmq@dev02 mq21]$./app_start.sh


[rabbitmq@dev02 mq22]$ ./app_stop.sh

[rabbitmq@dev02 mq22]$ ./ctl.sh join_cluster mq11@dev01

[rabbitmq@dev02 mq22]$ ./app_start.sh


[Mirroring] - Cluster 되면 아무곳에서 해도 됨

./ctl.sh set_policy 사용해도 되고, 그냥 "admin Tab" -> "Policies" 메뉴에서 UI상으로 하는게 편리!!

애래 Node 부분에 "+3" 부분이 보여야 함(정상/비정상 여부에 따라서 복제된 정보가 파랑숫자, 빨강숫자로 표현됨)

장애 후 복구된 서버에 이전데이터 동기(복제) 처리는 하지 않았음




[웹 접속 계정 생성 및 권한 부여] - Cluster 되면 아무곳에서 해도 됨

[rabbitmq@dev01 mq11]$ ./ctl.sh add_user test 1234

[rabbitmq@dev01 mq11]$ ./ctl.sh set_user_tags test administrator

그냥 "admin Tab" -> "Users" 메뉴에서 UI상으로 하는게 편리!!



앞단에 L4 를 붙이면 좋겠음








로그성 DB INSERT로 인해서 전체적인 DB성능 이슈가 발생하면서 어플리케이션까지 서능에 영향이 미치는 상황이 발생하였다.


처음에는 Logging 라이브러리를 활용하여 Text형태로 쌓아놓고 한가한 시간에 배치로 DB에 INSERT하려고 하다가 공부 겸 JMS(java message service)를 적용해보기로 하였다.


Message Queue 관련 방법은 여러가지가 있던데 선택은 Apache ActiveMQ 를 선택 하였다.

관련 장단점에 대해서는 인터넷 참고!!





우선 Apache ActiveMQ 설치


context-jms.xml


	



	



	



	
    




	
	
	



	
	
	
		
	


MemberMessageConverter.java

package pe.kr.ddakker.jms;

import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Session;

import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.jms.support.converter.MessageConverter;

public class MemberMessageConverter implements MessageConverter {

	public MemberMessageConverter(){}
	
	@Override
	public Object fromMessage(Message message) throws JMSException,
			MessageConversionException {
		if(!(message instanceof MapMessage)){
			throw new MessageConversionException("Messaeg isn't a MapMessage");
		}
		
		MapMessage mapMessage = (MapMessage) message;
		Member member = new Member();
		member.setName(mapMessage.getString("name"));
		member.setEmail(mapMessage.getString("email"));
		return member;
	}

	@Override
	public Message toMessage(Object object, Session session) throws JMSException,
			MessageConversionException {
		if(!(object instanceof Member)){
			throw new MessageConversionException("Messaeg isn't a Member");
		}
		
		Member member = (Member) object;
		MapMessage message = session.createMapMessage();
		message.setString("name", member.getName());
		message.setString("email", member.getEmail());
		return message;
	}

}


ReceiverMessageListenerImpl.java

package pe.kr.ddakker.jms;

import javax.annotation.Resource;
import javax.jms.Message;
import javax.jms.MessageListener;

import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.stereotype.Component;

@Component
public class ReceiverMessageListenerImpl implements MessageListener {


	@Resource
	private MessageConverter memberConverter;

	@Override
	public void onMessage(Message message) {
		Member member = null;
		try {
			member = (Member) memberConverter.fromMessage(message);

			if (member != null) {
				System.out.println("member.getEmail(): " + member.getEmail());
			}
		} catch (Exception e) {
			System.err.println(e);
		}

	}

}


JmsTest .java

package pe.kr.ddakker.jms;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath*:config/spring/context-jms.xml"})
public class JmsTest {
	@Resource
	private JmsTemplate jmsTemplate;
	
	@Test
	public void test() {
		
		for (int i = 0; i < 5; i++) {
			Member member = new Member();
			member.setName("ddakker" + i);
			member.setEmail("ddakker" + i + "@gmail.com");
			
			jmsTemplate.convertAndSend(member);
		}
	}
}


+ Recent posts