[MINA] Logging Configuration

개발 이야기/MINA | 2009. 11. 30. 10:27
Posted by 시반

팀내에서 MINA를 사용하면서 "선배 log4j가 있는데 계속 logging 쪽에서 에러가 떠요" 라는 질문을 받은 적이 있습니다. 지난 포스팅에서도 설명한적이 있지만 MINA 샘플들은 log4j 가 아닌 slf4j를 사용하고 있습니다. 사용법은 동일하지만 log4j보다 유연한 로깅메시지 처리를 할 수 있기 때문에 slf4j 라이브러리를 사용하였다고 하네요. 따라서 slf4j 라이브러리를 추가해야 정상적으로 샘플들을 구동할 수 있습니다.


Logging Configuration


이번 장에서는 MINA가 구현하고 있는 로깅처리부분을 살펴보도록 하겠습니다.

slf4j는 관련페이지를 참조하시길..

목차


  • 목차
  • 배경
  • SLF4J
    •  올바른 JARs 파일 선택하기
    • Jakarta Commons Logging 상속
  • log4j 예제

배경

 

Logging Confuguration  로깅환경설정에 있어서 기존의 log4j 의 환경을 그대로 사용할 수 있습니다.

SLF4J


배경에서 간단하게 언급은 했는데요 MINA가 slf4j 를 사용한다고 해서 특별한 로깅관련 설정이 필요한 것은 아니라는 점입니다. (SLF4J에 대한 정보는 이곳 에서 확인할 수 있습니다).

솔직히 특별한 로깅관련 설정이 필요없다기 보다 기존의 설정을 그대로 사용한다고 할까요. 대부분 log4j 또는 java.util.logging 또는 다른 logging관련 유틸리티를 사용하고 있을 테지만 slf4j은 그것들을 위한 간단한 facade 혹은 추상계층으로써 제공되기 때문입니다.
다시 말해서, slf4j는 엔드 유저가 희망하는 로깅 프레임워크를 배포시간에 플러그인할 수 있도록 허용하는 구조를 가지게 되기 때문에 개발중에 java.util.logging 에서 log4j로 변경해도 기존 소스의 변경이 없게 됩니다.

올바른JARs 파일 선택하기

SLF4J 은 정적 바인딩을 사용하고 있습니다. 이 말은 각 로깅 프레임워크에서 지원하는 단 하나의 JAR파일만이 있어야 한다는 것으로 사용하고 있는 버젼에 맞는 라이브러리만 추가하여야 합니다. 아래는 사용하고 있는 로깅 프레임워크에 따라 필요한 slf4j 파일들입니다.

Logging Framework
Requried JARs
Log4J 1.2.x
 slf4j-api.jar, slf4j-log4j12.jar
 Log4J 1.3.x
 slf4j-api.jar, slf4j-log4j13.jar
 java.util.logging   slf4j-api.jar, slf4j-jdk14.jar
 commons Logging
  slf4j-api.jar, slf4j-jcl.jar


주의사항 :

  • 보통 slf4j-api.jar 이 다른 JARs 내에서 이미 구현되어 사용되어 질 수도 있습니다
  • 중요한 건 당신이 당신의 클래스패스안에 하나 이상의 JAR 파일을 넣지 않는 것입니다 
    (예를 들면. slf4j-log4j12.jar slf4j-jdk14.jar 를 함께 넣지 마세요 그것은 예기치 못한 문제가 발생될지도 모릅니다
  • slf4j-api.jar slf4j-<impl>.jar 의 버전은 동일해야 합니다.

제대로 구성된 환경설정은 사용되는 로깅시스템이 유지하는 한 계속 사용할 수 있습니다(e.g. 수정된 log4j.properties).

 Jakarta Commons Logging 상속하기

SLF4J 은 또한 이미 Jakarta Commons Logging 을 사용하는 어플리케이션의 소스 변경없이 SLF4J 를 사용하도록 변환 할수 있는 방법을 제공합니다. 단지 클래스패스에서commons-loggong JAR 파일을 지우고 jcl104-over-slf4j.jar 를 추가하기만 하면 됩니다.


log4j 예제


이 예제는 log4j로 로깅 시스템을 구성하고 있습니다. 우리는 log4j.properties 를 열어 아래의 내용을 붙여 넣어 보겠습니다.


 # Set root logger level to DEBUG and its only appender to A1.
 log4j.rootLogger=DEBUG, A1

 # A1 is set to be a ConsoleAppender.
 log4j.appender.A1=org.apache.log4j.ConsoleAppender

 # A1 uses PatternLayout.
 log4j.appender.A1.layout=org.apache.log4j.PatternLayout
 log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c{1} %x - %m%n


이 파일은 프로젝트의 소스폴더에 있습니다. 만약 당신이 IDE를 사용하고 있다면 기본설정은 당신의 코드를 테스트하고자 할 때의 JVM상의 클래스패스안의 설정값으로 사용됩니다.

샘플예제는 지난 포스팅 예제인 MinaTimeServer 에 있는 예제를 가지고 설명합니다.:


 public
static void main(String[] args) throws Exception {
        IoAcceptor acceptor =
new SocketAcceptor();
        DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();


        LoggingFilter loggingFilter =
new LoggingFilter();
       
chain.addLast(
"logging", loggingFilter);                      
 

       
acceptor.setLocalAddress(
new InetSocketAddress(PORT));
       
acceptor.setHandler(
new EchoProtocolHandler());
       
acceptor.bind();

       
System.out.println("Listening on port " + PORT);
 }


위 예제에서는 IoAcceptor를 가지고 로깅하는 방법을 설명하고 있습니다. 어딘고 하니 acceptor에  LoggingFilter를 추가하는 부분이네요. 이 부분이 MINA가 slf4j를 구현한 부분으로 log4j를 사용하던지 commons-logging을 사용하던지 코드엔 변경이 없게 됩니다.

위에서는 간단한 로깅하는방법만을 예로 들었지만 좀더 요구사항에 따라 다양한 로깅정보를 생성할 수 있습니다.

LoggingFilter 레퍼런스에 따르면 IoAccepter 에 연결된 핸들러에 이벤트별로 로깅수준을 설정할 수 있습니다.
LoggingFilter에  setLogLevel(IoEventType,LogLevel) 이라는 메소드를 제공하고 있는데요.
IoHandler 이벤트 로깅을 추적하고 로깅수준에 따라 로그를 남길수 있습니다.

이 메소드의 옵션들은 다음과 같습니다:

IOEventType
Description
 SESSION_CREATED    새로운 세션을 생성할 때 호출된다
 SESSION_OPENED    새로운 세션이 열린 경우 호출된다
 SESSION_CLOSED    세션이 닫힌 경우 호출된다
 MESSAGE_RECEIVED    데이터를 받았을 때 호출된다
 MESSAGE_SENT    메시지를 전송할 때 호출된다
 SESSION_IDLE    세션 유휴타임에 도달한 때 호출된다
 EXCEPTION_CAUGHT    예외를 발생한 경우 호출된다

다음은 로깅 수준에 대한 설명입니다:

Log Level
Description
 NONE    설정과는 상관없이 로그를 남기지 않는다
 TRACE    로깅시스템에 추적 이벤트를 생성한다
DEBUG    로깅시스템에  debug 메시지를 남긴다.
INFO    로깅시스템에  info 메시지를 남긴다.
 WARN
   로깅시스템에  warning 메시지를 남긴다.
 ERROR    로깅시스템에  error 메시지를 남긴다.

'개발 이야기 > MINA' 카테고리의 다른 글

MINA에서 버퍼 제어하기 - IoBuffer  (0) 2009.06.01
MINA 기반의 어플리케이션 구조  (0) 2009.05.28
MINA 2.0 에코서버 만들기  (0) 2009.05.21
 

IoBuffer

IoBuffer는 MINA 어플리케이션에서 사용되는 Byte Buffer를 말하는데 NIOByteBuffer 를 대체하고 있으며 대체 이유로 다음 두가지를 들고 있습니다

  • fill, get/putString 그리고 get/putAsciiInt() 와 같은 유용한 getter/setter 메소드를 제공하지 않는다
  • ByteBuffer 의 허용크기가 가변길이 데이터를 저장하기에 충분하지 않다

IoBuffer는 앞서 말했지만 MINA에서 NIO ByteBuffer를 대체하는 클래스입니다. 그 이유는 버퍼의 확장을 쉽게 하기 위해서인데요.

좀더 자세히 말하자면 MINA 에서 IO를 최소화하기 위해 데이터를 한번만 읽어 모든 처리를 하자는 취지에서 만들어진 것 같습니다.

한마디로 소켓으로부터 받은  데이터를 버퍼에 담아 한번에 처리하겠다는 것인데요 .

문제는 고정길이가 아닌 가변길이의 데이타가 전송되는 경우 버퍼크기를 초과하는 데이터를 처리하기가 쉽지 않다는 것이었습니다.

그래서 버퍼의 확장이 쉽도록 구현된 대체 클래스인 셈이죠. 하지만 메모리 관리방법을 JVM내 객체가 저장되는 공간인 Heap  영역이 아닌 일반메모리상에서 직접 접근할 수 있는 버퍼영역인  Direct Buffer를 사용하는 경우에 문제가 발생하게 됩니다.

즉 Buffer의 데이터에 다른 프로그램으로 직접 접근할 수 있다는 보안상의 결함을 가지고 있다는 것이죠.

따라서 MINA3  버젼부터는 변경할 예정이라고 하네요.(원문 참조, Direct Buffer가 아닌 Heap 버퍼 사용을 권장함)

MINA에서 IoBuffer를 사용하는 방법

IoBuffer 는 추상클래스입니다, 따라서 직접 인스턴스화 할 수는 없습니다. IoBuffer를 할당하기 위해서 우리는 두개의 allocate() 메소드 중 하나를 사용해야 합니다.

  • public static IoBuffer allocate(int capacity, boolean direct)
  • public static IoBuffer allocate(int capacity)

위에서 처럼 allocate() 메소드는 하나 또는 두개의 인자를 가집니다. :

  • capacity버퍼 크기 
  • direct버퍼 유형. direct buffer를 얻기 위해서는 true, heap bufferfalse

기본적으로 버퍼의 할당은  SimpleBufferAllocator 에 의해 핸들링되어 처리됩니다. 아래와 같은 방법으로도 사용될 수 있습니다

  • IoBuffer.setUseDirectBuffer(false);
  • IoBuffer buf = IoBuffer.allocate(1024);

즉 기본값으로 Heap버퍼를 사용하는 것으로 설정한 후 새로운 Heap 버퍼로 반환하는 것입니다. 이 방법을 사용하기 전에 주의할 점은 기본 버퍼유형을 설정해야 한다는 것입니다. 그래야만 Heap 버퍼를 사용하게 됩니다.(앞서 말한 이유로...)

 

잠깐 위에서 언급했지만 MINA에서 버퍼를 할당 및 관리를 IoBufferAllocator 에서 처리하도록 되어 있는데요. 다음 두가지 방법을 지원하고 있습니다. setAllocator()를 통해 설정할 수 있구요. 직접 IoBufferAllocator를 구현할 수도 있습니다.

  • SimpleBufferAllocator(기본값) - 매번 새로운 버퍼를 생성한다 
  • CachedBufferAllocator - 확장시 재사용가능한 버퍼영역을 캐싱한다.

예제

 

먼저 자동으로 버퍼를 확장하는 방법에 대한 예제 입니다.  IoBuffer는  autoExpand라는 프로퍼티를 이용하여 버퍼크기와 한계치를 자동으로 확장할수 있습니다.


IoBuffer buffer = IoBuffer.allocate(8);

buffer.setAutoExpand(true);

 

buffer.putString("12345678",encoder);

 

//버퍼에 추가

buffer.put((byte)10);

버퍼를 증가시키는 것은 어렵지 않습니다. 마치 StringBuffer 를 통해서 문자열을 추가하듯이 버퍼에 put메소드를 사용하면 자동적으로 증가가 됩니다.

 

지속적으로 버퍼가 증가만 된다면 문제겠죠. 메모리자원을 보존하기 위해 추가할당된 byte 들을 삭제하도록 요청할 필요가 있습니다. 이러한 요구에 따라 autoShrink 프로퍼티를 지원하고 있는데요. autoExpand 와는 반대의 개념으로 compact() 메소드가 호출되면 버퍼의 크기를 절반 또는 현재 할당된 영역의 4분지1이하의 크기로 줄일 수 있습니다. 다음은 버퍼 크기를 수동으로 줄이는 예제입니다.

 


IoBuffer buffer = IoBuffer.allocate(8);

buffer.setAutoExpand(true);

 

buffer.put((byte)1);

System.out.println("기본 버퍼 크기 = "+buffer.capacity());

buffer.shrink();

System.out.println(" shrink  이후 기본 버퍼 크기 = "+buffer.capacity());

 

buffer.capacity(32);

System.out.println("버퍼 크기를 32로 증가시킨 결과 = "+buffer.capacity());

buffer.shrink();

System
.out.println("shrink  이후 버퍼 크기 = "+buffer.capacity());

결과

기본 버퍼 크기 = 16 // 버퍼 크기가 16인 버퍼 생성. 즉 버퍼의 최소용량을 16으로 설정

shrink 후 기본버퍼 크기 = 16 // shrink() 호출 . 하지만 최소영역 이하로 줄일수 없습니다. 16

버퍼크기를 32로 증가시킨 결과 = 32 // 버퍼의 최대크기를 32로 늘임. 버퍼용량 32

shrink 이후 버퍼크기 =16 // shrink() 호출. 기본최소용량인 16으로 버퍼크기를 줄임

버퍼크기를 수동으로 줄이고자할때 shrink()를 사용하고 있지만 버퍼의 크기를 줄이기 위해 이 예제에서처럼 명시적으로 요청할 필요는 없습니다.

앞서 말한바와 같이 autoShrink 프로퍼티를 설정하면 자동적으로 메모리자원을 확보하기 위해 추가된 버퍼를 삭제할 수 있습니다.

 

이 글은 http://mina.apache.org/iobuffer.html 를 참고하여 작성되었구요.

좀더 상세하게 설명되어 있는 번역문은 pdf파일로 첨부하였습니다만. 언제나 그렇듯이 번역상태는 ....-_-a

'개발 이야기 > MINA' 카테고리의 다른 글

[MINA] Logging Configuration  (0) 2009.11.30
MINA 기반의 어플리케이션 구조  (0) 2009.05.28
MINA 2.0 에코서버 만들기  (0) 2009.05.21
 

MINA 기반의 어플리케이션 구조

개발 이야기/MINA | 2009. 5. 28. 18:01
Posted by 시반

MINA 기반의 어플리케이션 구조

개요                                                                                                            

MINA 기반의 어플리케이션의 구조에 대하여 많은 분들이 질문해오셨습니다. 기사를 통해 MINA 기반 어플리케이션이 어떤 아키텍처를 가지고 있는지 살펴보도록 하겠습니다.MINA 관련된 프레젠테이션 자료에서 발췌했습니다.

조감도 :

 

좀더 상세하게 알아볼까요?

 

이미지는 Apache MINA 이용하여 Network Application 빨리 개발하기(JavaOne 2008) 라는 이희승님의 발표자료에서 가져왔습니다.

크게 보면, MINA 기반의 어플리케이션은 3개의 레이어로 구분되어 집니다

·         I/O Service - 실제 I/O 수행하는 부분

·         I/O Filter Chain - 필터/변환, 바이트를 원하는 데이터 구조로 변환(역으로도 가능)

·         I/O Handler실제 비즈니스 로직이 들어가게 되는 부분

그럼 이제 어떻게 MINA 기반의 어플리케이션을 개발하는지 살펴 볼까요

1.    Create I/O service기본으로 제공하는 서비스 (*Acceptor)  또는 스스로 만든 I/O  서비스 중에서 선택합니다

2.    Create Filter Chain요청/응답간의 변환을 처리하기 위해 필터를 지정합니다. 필터는 기본으로 제공하는 필터 사용자 필터 중에서 선택합니다

3.    Create I/O Handler각기 다른 메시지들을 핸들링 하기 위한 비즈니스 로직을 작성합니다.
MINA
어플리케이션을 만드는 것은쉽죠~? ##########2*

 

ps. 마찬가지로 미나 레퍼런스를 번역한 글입니다.

원문은 http://mina.apache.org/mina-based-application-architecture.html 에서 확인할 수 있습니다.

'개발 이야기 > MINA' 카테고리의 다른 글

[MINA] Logging Configuration  (0) 2009.11.30
MINA에서 버퍼 제어하기 - IoBuffer  (0) 2009.06.01
MINA 2.0 에코서버 만들기  (0) 2009.05.21
 

MINA 2.0 에코서버 만들기

개발 이야기/MINA | 2009. 5. 21. 11:17
Posted by 시반

이번에 소개하고자 하는것은 이희승씨가 커미터로 계셨다는 

MINA(A Multi-purpose Infrastructure for Network Applications)입니다.

간단하게 말하면 자바의 네트워크 애플리케이션을 위한 프레임워크입니다.
필터를 사용한 뛰어난 확장성과 프로토콜 코덱과 비즈니스 로직을 분리하여 유지보수와 재사용성을 높인것이 특징인데요.
한글레퍼런스나 튜토리얼을 기대했는데 역시 아파치 재단이랄까?

이희승씨를 믿고 있건만 영어의 압박이 저를 힘들게 하네요...-_- a.

하지만 사용법은 그다지 어렵지 않은 듯 보입니다.간단하게 예제 프로그램을 따라 만들어 보았습니다.

MINA v2.0 Quick Guild를 참고하였으며. 현재 버젼은 M5까지 나와있습니다 기존의 문제점을 개선한 MINA3을 준비중이라고 하네요.
일단 예제를 위해서는 MINA코어뿐만 아니라 SLF4J Log4J도 필요로 하고 있구요.
SLF4J의 경우에는 1.5.6 버젼까지 나와있는 상태인데 사용하는 Log4J버젼에 따라 선택하면 됩니다.

slf4j-api-1.5.6.jar파일과 slf4j-logj12-1.5.6.jar을 사용합니다. log4j가 1.2버젼이라서.....
기타 JDK6.0, Eclipse3.4 환경에서 테스트합니다.

 
MinaTimeServer

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
 
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
 
public class MinaTimeServer
{
   
private static final int PORT = 9123;
 
   
public static void main(String[] args) throws IOException
   
{
       
IoAcceptor acceptor = new NioSocketAcceptor();

        acceptor
.getFilterChain().addLast( "logger", new LoggingFilter() );
        acceptor
.getFilterChain().addLast("codec", new ProtocolCodecFilter(
                                           
new TextLineCodecFactory(Charset.forName("UTF-8"))));
 
        acceptor
.setHandler(new TimeServerHandler());
 
        acceptor
.getSessionConfig().setReadBufferSize(2048);
        acceptor
.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
 
        acceptor
.bind(new InetSocketAddress(PORT));
   
}
}


Non-Blocking IO로 만듭니다. 클라이언트의 연결 및 실시간 요청을 처리할 리스너를 등록합니다.

IoAcceptor 인터페이스로 구현된 acceptor 객체에 하나이상의 필터들을 등록할 수 있는데 이것을 FilterChain이라 합니다.

이 필터체인이라는 것에 addLast를 통해 필터들을 등록합니다.

위와 같이 로깅을 하거나 인코딩 코덱을 만들어 사용할 수 있습니다.
핸들러를 추가하고 버퍼와 유휴시간을 정의한 후에 PORT를 설정하여 Bind합니다.

TimeServerHandler

import java.util.Date;
 
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
 
public class TimeServerHandler extends IoHandlerAdapter
{

   
@Override
   
public void exceptionCaught(IoSession session, Throwable cause)
       
throws Exception
   
{
        cause
.printStackTrace();
   
}

   
@Override
   
public void messageReceived(IoSession session, Object message)
       
throws Exception
   
{
       
String str = message.toString();
 
       
if(str.trim().equalsIgnoreCase("quit"))
       
{
            session
.close(true);
           
return;
       
}
 
       
Date date = new Date();
        session
.write(date.toString() + "\r");
       
System.out.println("Message written...");
   
}

   
@Override
   
public void sessionIdle(IoSession session, IdleStatus status)
       
throws Exception
   
{
       
System.out.println("IDLE " + session.getIdleCount(status));
   
}
}


이녀석이 실제로 통신에 사용되는 핸들러입니다. 메시지를 받을때 마다 messageReceived이 호출됩니다.
쓰레드를 이용하고 있으며, 콜백형식으로 작동하는 것을 알 수 있습니다.

sessionIdle을 사용하면 Main에서 정의한 유휴시간마다 호출됩니다.

앞서 10초로 설정해 두었으니 10초동안 유휴상태로 있다면 그때마다 이 함수가 호출됩니다.

getIdleCount를 이용해 얼마나 유휴상태에 있는지를 확인하도록 합니다. 서버를 완성했으니 실행해 볼까요.

c:>telnet localhost 9123

 


quit를 입력하면 종료되고 그 이외의 문자를 입력하면 시간이 출력되는 것을 알 수 있습니다.

한글을 입력시 MalformedInputException이 나면서 동작안하는 경우가 있는데

인코딩타입을 UTF-8로 바꾸어주면됩니다.

번역한 튜토리얼은 첨부파일로 첨부합니다. 최대한 매끄러게 번역을 하려고 했는데. 앞서 이야기했지만 영어가 딸려놔서리..

오역이나 이상한 것들은 기냥 구박마시고 조용히 알려주세요..*^^*

 

'개발 이야기 > MINA' 카테고리의 다른 글

[MINA] Logging Configuration  (0) 2009.11.30
MINA에서 버퍼 제어하기 - IoBuffer  (0) 2009.06.01
MINA 기반의 어플리케이션 구조  (0) 2009.05.28
 
블로그 이미지

시반

시반(詩伴)이란 함께 시를 짓는 벗이란 뜻을 가지고 있습니다. 함께 나눌수 있는 그런 공간이길 바라며...

카테고리

분류 전체보기 (233)
개발 이야기 (73)
WEB2.0 (57)
DB2 (24)
MySQL (6)
오라클 (26)
기타 (44)
취미 (0)
잡담 (2)