ACE Framework에 사용된 패턴들
[출처] http://ppiazi.springnote.com/pages/320279
목차
Writer : 이주현(Joohyun Lee : ppiazi at lignex1.com)
1. Abstract#
이 문서는 전자전 프레임워크(Electronic Warfare Framework) 개발이라는 장기적인 목표를 위한 사전 기술 조사 자료를 위해 쓰여졌으며, 특히 기존의 네트워크 프레임워크 중 ACE Framework(ADAPTIVE Communication Environment) 를 분석하여 기존의 프레임워크들이 가지고 있는 특징을 분석하는데 촛점을 맞추었다. ACE 프레임워크를 구성하고 있는 중요한 패턴들을 분석하고 이해함으로써 객체 지향형 개발 방법론으로 프로젝트를 진행시 개발자들간의 의사소통을 도와 상호간의 이해를 쉽게 할 수 있고, 시스템을 보다 견고하고 확장성있게 구축할 수 있다.
2. Introduction#
Concurrent 하고 다양한 시스템과
3. Patterns#
3.1 Reactor#
3.1.1 Intent#
Reactor 패턴은 event-driven 어플리케이션을 구조화하는 패턴이다. 단일 쓰레드에서 여러개의 소스로부터 동시적으로 요청들을 수신하여 그들의 요구를 처리한다. 다음의 요구에 의해 Reactor 패턴을 적용한다.
- 단일 제어 쓰레드에서 여러개의 소스로 부터의 다양한 종류의 이벤트들을 수신하여처리하려고 할 때
- 어플리케이션을 확장할 때 이벤트 처리 프레임워크에는 영향을 미치지 않게 하려고 할 때
3.1.2 Structure & Dynamics#
위의 그림은 Reactor 패턴의 구조도를 보여주고 있다. Reactor 패턴은 Event Handler를 실제로 구현한 구현 클래스(Concrete Event Handler)들을 등록, 해제, 호출을 위한 인터페이스를 가지고 있다.Event Handler는 Reactor 패턴이 필요로 하는 추상 인터페이스들을 가지고 있어 특정 어플리케이션에 비의존적인 매카니즘을 제공한다. 다시말하여, 이러한 Reactor와 Event Handler의 관계는 개발자의 비지니스 로직 부분을 이벤트 핸들링과 분리를 함으로써 어떠한 Event-driven 상황이 필요한 곳에 Reactor패턴을 적용시킬수 있다. Event Handler는 input, output, timer_events 등 여러가지 조건에 대해 반응할 수 있도록 인터페이스를 제공하고 있다.
위의 그림은 Reactor 패턴이 다른 객체들과 어떻게 상호 작용하는 지를 보여주는 시퀀스 다이어그램이다.
- Initialization mode : Concrete Event Handler 객체가 Reactor 객체에 등록된다.
- Event Handling Mode : select(유닉스 시스템의 경우)나 WaitForMultipleObjects(윈도우 시스템의 경우)를 사용하여 외부 소스로 부터 변경된 사항이 있는지 확인하여, 만약 처리해야할 이벤트가 감지 되었을 경우 등록된 Concrete Event Handler 객체의 해당 메쏘드를 역호출하여 처리한다.
3.2 Acceptor & Connector#
Intent#
Acceptor & Connector 프레임워크는 일반적인 연결 처리 작업과 신규 연결에서의 서비스 실행을 위한 서비스 핸들러를 생성하는 작업을 구현한 것이다. 또한, 프레임워크의 주요 클래스들은 별도의 템플릿 매개변수 정의를 통하여 내부 처리 방식의 대부분을 변경 가능하도록 디자인되어 있다.
3.3 Proactor#
3.4 Active Object#
3.4.1 Intent#
Active Object 패턴은 동시성을 향상시키고, 그들 자신의 제어 흐름안에 있는 오브젝트에 대해 접근하기 위한 동기화 방법을 간단히하기 위해서 함수의 실행 부분과 함수의 호출 부분을 분리한 패턴이다.
3.4.2 Structure#
Active Object는 아래와 같은 6가지의 구성품으로 이루어져 있다.
3.4.2.1 Proxy#
요청을 실행하기 위해 클라이언트에 의해 사용될 수신자 객체에 대한 프록시 객체를 의미한다. 클라이언트로 하여금 Active Object에 접근할 수 있는 인터페이스를 제공한다.이 클래스는 메쏘드 호출을 메쏘드 요청(Method Request)으로 변환하여 새로운 객체를 생성하고, 이를 활성 큐(Activation Queue)에 삽입하는 역할을 한다.프록시 객체는 실제 수신자 구현 객체에 대한 참조를 내장하고 있다.
3.4.2.2 Scheduler#
Scheduler 클래스는 Active Object의해 사용되는 내부 쓰레드가 새로운 메소드 요청이 도착할 때까지 블럭 상태에 놓일 때 사용하기 위한 활성 큐(Activation Queue)에 대한 참조를 가지고 있다. 요청이 도착하면 내부 쓰레드는 깨어나고, 요청을 큐로부터 추출한 후 그것을 실행한다. 메쏘드 요청의 Guard를 사용하여 해당 메쏘드 요청이 실행가능한지 가능하지 않은지를 판단하게 된다.
3.4.2.3 Activation List#
Scheduler가 소유하고 있는 우선순위 큐를 지칭한다. Proxy 객체에의해 이 큐에 적재되며, Active Object의 내장 쓰레드에 의해 추출된다.
3.4.2.4 Method Request#
Method Request는 특정 함수 호출을 실행하거나 값을 도출해내기 위한 여러 가지 파라미터 정보를 포함한다. 또한 Method Request 클래스는 Active Object의 실행 메쏘드를 위한 인터페이스를 정의한다. (Command 패턴)
3.4.2.5 Servant#
Active Object로 모델링 된 행동양식과 상태정보를 구현한다. Servant가 구현하는 메쏘드들은 Proxy의 인터페이스 및 Proxy가 생성하는 Method Request에 대응된다.
3.4.2.6 Future#
클라이언트에 의해 반환되는 비동기 명령의 결과를 얻기 위해 나중에 (결과값으로) 교환할 수 있는 토큰 객체를 의미한다.
3.4.3 Dynamics#
- Method Request construction : 이 단계에서는 클라이언트가 Proxy에 의해 정의된 함수를 호출한다. 이 호출은 요구 사항을 수행하기 위해 필요한 파라미터들을 가지고 있는 Method Request를 생성하여 Scheduler의 Activation List에 등록한다. 동시에 Future 객체를 클라이언트에게 반환한다.
- Scheduling / Execution : Scheduler는 Mutual Exclusion Lock을 획득하고 Activation List에 요청하여 어떤 Method Request가 Synchronization Constraints를 만족하는지 선택한다. 선택된 Method Request는 Servant에 바인딩되며 임무를 수행한다.
- Return result : 이 단계에서는 Method Request의 결과와 클라이언트에게 반환된 Future를 바인드한다.
3.5 Component Configurator#
3.5.1 Intent#
Component Configurator 패턴은 어플리케이션이 실행시 원하는 컴포넌트들을 어플리케이션의 중단없이 동적으로 링크하거나 언링크 하는 방법을 제공한다. 또한 동작하는 어플리케이션의 재컴파일 또는 중단없이 존재하는 컴포넌트를재설정할 수 있도록 한다.다음의 요구사항이 있을 때이 패턴의 사용을 고려한다.
- 시스템을 디자인 하는데 있어서 컴포넌트의 특정 구현에 대한 방법을 최대한 미뤄야할 때
- 독립적으로 구축된 컴포넌트들을 선택적으로 조합하여 사용하거나 스크립트에 의해 컴포넌트들의 동작을 제어하고 싶을 때
3.5.2 Structure#
Component Configurator 패턴을 적용하면 컴포넌트 인터페이스와 그들의 실제 구현 부분을 분리하여 구현된 컴포넌트들이 어플리케이션에 연결되어 사용되어지는 시점과 어플리케이션의 구현 시점을 서로 의존적이지 않도록 만든다. Compoent Configurator 패턴은아래의4가지의 참여자(participant)를 포함한다.
3.5.2.1 Component#
Component는 컴포넌트 구현과 관련되어, 컴포넌트들이 어플리케이션에 포함되기 위해 설정되어질 때 필요한 인터페이스들과 각 컴포넌트들이 제공하는 서비스들에 대한 인터페이스를 가지고 있어야 한다.
3.5.2.2 Concrete Component#
Concrete Component는 이러한 Component를 상속받아 구체적으로 어플리케이션 로직을 구현해야 한다. Concrete Component들은 실행중인 어플리케이션에 동적으로 포함되기 위해서동적 라이브러리의 형태로(예를 들어 DLL) 패키징되어야 한다.
3.5.2.3 Component Repository#
Component Repository는 어플리케이션에 사용되는 모든 Concrete Component들을 관리한다. Component Repository는 시스템 관리자로 하여금 Concrete Component들을 관리할 수 있는 중앙집중적인 제어를 가능하게 한다.
3.5.2.4 Component Configurator#
Component Configurator는Component Repository를 관리하며, Concrete Component들을 초기화하여 Component Repository에 저장할 수 있으며, 변경된 Component들을 다시 읽어 재수행할 수 있으며, 필요하지 않은 Component들은 Component Repository에서 제거할 수 있다.
3.5.3 Dynamics#
4. Conclusion#
5. References#
- Pattern-Oriented Software Architecture Volume 2 - Patterns for Concurrent and Networked Objects
- Applying a Pattern Language to Develop Application-level Gateways
Example Source Codes#
Example Source Code - Reactor, Acceptor, Connector#
- // HAStatus.cpp,v 1.6 2005/01/21 02:19:19 ossama Exp
#include "ace/OS_NS_sys_time.h"
#include "ace/os_include/os_netdb.h"
// Listing 1 code/ch07
#include "ace/Auto_Ptr.h"
#include "ace/Log_Msg.h"
#include "ace/INET_Addr.h"
#include "ace/SOCK_Acceptor.h"
#include "ace/Reactor.h"
class ClientAcceptor : public ACE_Event_Handler
{
public:
virtual ~ClientAcceptor ();
int open (const ACE_INET_Addr &listen_addr);
// Get this handler's I/O handle.
virtual ACE_HANDLE get_handle (void) const
{ return this->acceptor_.get_handle (); }
// Called when a connection is ready to accept.
virtual int handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE);
// Called when this handler is removed from the ACE_Reactor.
virtual int handle_close (ACE_HANDLE handle,
ACE_Reactor_Mask close_mask);
protected:
ACE_SOCK_Acceptor acceptor_;
};
// Listing 1
// Listing 6 code/ch07
#include "ace/Message_Block.h"
#include "ace/Message_Queue.h"
#include "ace/SOCK_Stream.h"
#include "ace/Synch.h"
class ClientService : public ACE_Event_Handler
{
public:
ACE_SOCK_Stream &peer (void) { return this->sock_; }
int open (void);
// Get this handler's I/O handle.
virtual ACE_HANDLE get_handle (void) const
{ return this->sock_.get_handle (); }
// Called when input is available from the client.
virtual int handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE);
// Called when output is possible.
virtual int handle_output (ACE_HANDLE fd = ACE_INVALID_HANDLE);
// Called when this handler is removed from the ACE_Reactor.
virtual int handle_close (ACE_HANDLE handle,
ACE_Reactor_Mask close_mask);
protected:
ACE_SOCK_Stream sock_;
ACE_Message_Queue<ACE_NULL_SYNCH> output_queue_;
};
// Listing 6
// Listing 5 code/ch07
ClientAcceptor: : ~ClientAcceptor ()
{
this->handle_close (ACE_INVALID_HANDLE, 0);
}
// Listing 5
// Listing 2 code/ch07
int
ClientAcceptor: : open (const ACE_INET_Addr &listen_addr)
{
if (this->acceptor_.open (listen_addr, 1) == -1)
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ("%p\n"),
ACE_TEXT ("acceptor.open")),
-1);
return this->reactor ()->register_handler
(this, ACE_Event_Handler: : ACCEPT_MASK);
}
// Listing 2
// Listing 3 code/ch07
int
ClientAcceptor: : handle_input (ACE_HANDLE)
{
ClientService *client;
ACE_NEW_RETURN (client, ClientService, -1);
auto_ptr<ClientService> p (client);
if (this->acceptor_.accept (client->peer ()) == -1)
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ("(%P|%t) %p\n"),
ACE_TEXT ("Failed to accept ")
ACE_TEXT ("client connection")),
-1);
p.release ();
client->reactor (this->reactor ());
if (client->open () == -1)
client->handle_close (ACE_INVALID_HANDLE, 0);
return 0;
}
// Listing 3
// Listing 4 code/ch07
int
ClientAcceptor: : handle_close (ACE_HANDLE, ACE_Reactor_Mask)
{
if (this->acceptor_.get_handle () != ACE_INVALID_HANDLE)
{
ACE_Reactor_Mask m = ACE_Event_Handler: : ACCEPT_MASK |
ACE_Event_Handler: : DONT_CALL;
this->reactor ()->remove_handler (this, m);
this->acceptor_.close ();
}
return 0;
}
// Listing 4
// Listing 7 code/ch07
int
ClientService: : open (void)
{
ACE_TCHAR peer_name[MAXHOSTNAMELEN];
ACE_INET_Addr peer_addr;
if (this->sock_.get_remote_addr (peer_addr) == 0 &&
peer_addr.addr_to_string (peer_name, MAXHOSTNAMELEN) == 0)
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("(%P|%t) Connection from %s\n"),
peer_name));
return this->reactor ()->register_handler
(this, ACE_Event_Handler: : READ_MASK);
}
// Listing 7
// Listing 8 code/ch07
int
ClientService: : handle_input (ACE_HANDLE)
{
const size_t INPUT_SIZE = 4096;
char buffer[INPUT_SIZE];
ssize_t recv_cnt, send_cnt;
if ((recv_cnt = this->sock_.recv (buffer, sizeof(buffer))) <= 0)
{
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("(%P|%t) Connection closed\n")));
return -1;
}
send_cnt =
this->sock_.send (buffer, static_cast<size_t> (recv_cnt));
if (send_cnt == recv_cnt)
return 0;
if (send_cnt == -1 && ACE_OS: : last_error () != EWOULDBLOCK)
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ("(%P|%t) %p\n"),
ACE_TEXT ("send")),
0);
if (send_cnt == -1)
send_cnt = 0;
ACE_Message_Block *mb;
size_t remaining =
static_cast<size_t> ((recv_cnt - send_cnt));
ACE_NEW_RETURN (mb, ACE_Message_Block (remaining), -1);
mb->copy (&buffer[send_cnt], remaining);
int output_off = this->output_queue_.is_empty ();
ACE_Time_Value nowait (ACE_OS: : gettimeofday ());
if (this->output_queue_.enqueue_tail (mb, &nowait) == -1)
{
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("(%P|%t) %p; discarding data\n"),
ACE_TEXT ("enqueue failed")));
mb->release ();
return 0;
}
if (output_off)
return this->reactor ()->register_handler
(this, ACE_Event_Handler: : WRITE_MASK);
return 0;
}
// Listing 8
// Listing 9 code/ch07
int
ClientService: : handle_output (ACE_HANDLE)
{
ACE_Message_Block *mb;
ACE_Time_Value nowait (ACE_OS: : gettimeofday ());
while (0 == this->output_queue_.dequeue_head
(mb, &nowait))
{
ssize_t send_cnt =
this->sock_.send (mb->rd_ptr (), mb->length ());
if (send_cnt == -1)
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("(%P|%t) %p\n"),
ACE_TEXT ("send")));
else
mb->rd_ptr (static_cast<size_t> (send_cnt));
if (mb->length () > 0)
{
this->output_queue_.enqueue_head (mb);
break;
}
mb->release ();
}
return (this->output_queue_.is_empty ()) ? -1 : 0;
}
// Listing 9
// Listing 10 code/ch07
int
ClientService: : handle_close (ACE_HANDLE, ACE_Reactor_Mask mask)
{
if (mask == ACE_Event_Handler: : WRITE_MASK)
return 0;
mask = ACE_Event_Handler: : ALL_EVENTS_MASK |
ACE_Event_Handler: : DONT_CALL;
this->reactor ()->remove_handler (this, mask);
this->sock_.close ();
this->output_queue_.flush ();
delete this;
return 0;
}
// Listing 10
// Listing 12 code/ch07
class LoopStopper : public ACE_Event_Handler
{
public:
LoopStopper (int signum = SIGINT);
// Called when object is signaled by OS.
virtual int handle_signal (int signum,
siginfo_t * = 0,
ucontext_t * = 0);
};
LoopStopper: : LoopStopper (int signum)
{
ACE_Reactor: : instance ()->register_handler (signum, this);
}
int
LoopStopper: : handle_signal (int, siginfo_t *, ucontext_t *)
{
ACE_Reactor: : instance ()->end_reactor_event_loop ();
return 0;
}
// Listing 12
// Listing 13 code/ch07
#include "ace/Signal.h"
class LogSwitcher : public ACE_Event_Handler
{
public:
LogSwitcher (int on_sig, int off_sig);
// Called when object is signaled by OS.
virtual int handle_signal (int signum,
siginfo_t * = 0,
ucontext_t * = 0);
// Called when an exceptional event occurs.
virtual int handle_exception (ACE_HANDLE fd = ACE_INVALID_HANDLE);
private:
LogSwitcher () {}
int on_sig_; // Signal to turn logging on
int off_sig_; // Signal to turn logging off
int on_off_; // 1 == turn on, 0 == turn off
};
LogSwitcher: : LogSwitcher (int on_sig, int off_sig)
: on_sig_ (on_sig), off_sig_ (off_sig)
{
ACE_Sig_Set sigs;
sigs.sig_add (on_sig);
sigs.sig_add (off_sig);
ACE_Reactor: : instance ()->register_handler (sigs, this);
}
// Listing 13
// Listing 14 code/ch07
int
LogSwitcher: : handle_signal (int signum, siginfo_t *, ucontext_t *)
{
if (signum == this->on_sig_ || signum == this->off_sig_)
{
this->on_off_ = signum == this->on_sig_;
ACE_Reactor: : instance ()->notify (this);
}
return 0;
}
// Listing 14
// Listing 15 code/ch07
int
LogSwitcher: : handle_exception (ACE_HANDLE)
{
if (this->on_off_)
ACE_LOG_MSG->clr_flags (ACE_Log_Msg: : SILENT);
else
ACE_LOG_MSG->set_flags (ACE_Log_Msg: : SILENT);
return 0;
}
// Listing 15
// Listing 11 code/ch07
int ACE_TMAIN (int, ACE_TCHAR *[])
{
ACE_INET_Addr port_to_listen ("HAStatus");
ClientAcceptor acceptor;
acceptor.reactor (ACE_Reactor: : instance ());
if (acceptor.open (port_to_listen) == -1)
return 1;
ACE_Reactor: : instance ()->run_reactor_event_loop ();
return (0);
}
// Listing 11
Example Source Code - Active Object #1#
- // future2.cpp, v 4.26 2005/04/30 06: 51: 28 ossama Exp
// ============================================================================
//
// = LIBRARY
// tests
//
// = FILENAME
// Test_Future.cpp
//
// = DESCRIPTION
// This example tests the ACE Future.
//
// = AUTHOR
// Andres Kruse <Andres.Kruse@cern.ch> and Douglas C. Schmidt
// <schmidt@cs.wustl.edu>
//
// Modification History
// Aug. 96; A.Kruse; dev.
// Aug. 96; D.Schmidt; complete workover
// 08/27/96; A.Kruse; - the friends of Scheduler are "Method_Request_name"
// and "Method_Request_work".
// - make the methods "work_i" and "name_i" private
// 09/2/96; D.Schmidt; Integrate with new ACE_Future API and rearrange
// the tests so they are more modular.
// ============================================================================
#include "ace/OS_NS_string.h"
#include "ace/OS_NS_sys_time.h"
#include "ace/OS_NS_unistd.h"
#include "ace/OS_main.h"
#include "ace/ACE.h"
#include "ace/Task.h"
#include "ace/Message_Queue.h"
#include "ace/Future.h"
#include "ace/Method_Request.h"
#include "ace/Activation_Queue.h"
#include "ace/Auto_Ptr.h"
#include "ace/Atomic_Op.h"
// Servant
class HA_ControllerAgent
{
public:
HA_ControllerAgent()
{
ACE_TRACE(ACE_TEXT("HA_ControllerAgent: : HA_ControllerAgent"));
status_result_ = 1;
}
int status_update()
{
ACE_TRACE(ACE_TEXT("HA_ControllerAgent: : status_update"));
ACE_DEBUG((LM_DEBUG, ACE_TEXT("Scheduler : Now making result %d - start service\n"), status_result_));
ACE_OS: : sleep(2);
ACE_DEBUG((LM_DEBUG, ACE_TEXT("Scheduler : Now making result %d - end service\n"), status_result_));
return next_result_id();
}
private:
int next_result_id()
{
ACE_TRACE(ACE_TEXT("HA_ControllerAgent: : next_cmd_id"));
return status_result_++;
}
int status_result_;
};
// Method Request
class StatusUpdate : public ACE_Method_Request
{
public:
StatusUpdate(HA_ControllerAgent & controller, ACE_Future<int> &returnVal): controller_(controller), returnVal_(returnVal)
{
ACE_TRACE(ACE_TEXT("StatusUpdate: : StatusUpdate"));
}
virtual int call(void)
{
ACE_TRACE(ACE_TEXT("StatusUpdate: : call"));
this->returnVal_.set(this->controller_.status_update());
return 0;
}
private:
HA_ControllerAgent &controller_;
ACE_Future<int> returnVal_;
};
// Method Request
class ExitMethod : public ACE_Method_Request
{
public:
virtual int call(void)
{
return -1;
}
};
// Scheduler
class Scheduler: public ACE_Task_Base
{
public:
Scheduler()
{
ACE_TRACE(ACE_TEXT("Scheduler: : Scheduler"));
this->activate();
}
virtual int svc(void)
{
ACE_TRACE(ACE_TEXT("Scheduler: : svc"));
while ( 1 )
{
auto_ptr <ACE_Method_Request> request(this->activation_queue_.dequeue());
if ( request->call() == -1 )
break;
}
return 0;
}
int enqueue(ACE_Method_Request *request)
{
ACE_TRACE(ACE_TEXT("Scheduler: : enqueue"));
return this->activation_queue_.enqueue(request);
}
private:
ACE_Activation_Queue activation_queue_;
};
// Proxy
class HA_ControllerAgentProxy
{
public:
ACE_Future<int> status_update(void)
{
ACE_TRACE(ACE_TEXT("HA_ControllerAgentProxy: : status_update"));
ACE_Future <int> result;
this->scheduler_.enqueue(new StatusUpdate(this->controller_, result));
return result;
}
void exit(void)
{
ACE_TRACE(ACE_TEXT("HA_ControllerAgentProxy: : exit"));
this->scheduler_.enqueue(new ExitMethod);
}
private:
Scheduler scheduler_; // 이 시점에서 scheduler 쓰레드가 시작한다.
HA_ControllerAgent controller_;
};
int ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
HA_ControllerAgentProxy controller;
ACE_Future<int> results[10];
for ( int i = 0; i < 10; i++)
results[i] = controller.status_update();
// take 5-minute break
ACE_OS: : sleep(5);
// and try to get a result from scheduler
ACE_DEBUG((LM_DEBUG, ACE_TEXT("Client : Let's get results!!\n")));
for ( int j = 0; j < 10; j ++ )
{
int result;
results[j].get(result);
ACE_DEBUG((LM_DEBUG, ACE_TEXT("Client : New status_update %d\n"), result));
}
controller.exit();
ACE_Thread_Manager::instance()->wait();
return 0;
}
'기본 카테고리' 카테고리의 다른 글
소스인사이트 단축키(2) (0) | 2010.10.11 |
---|---|
앱 스토어 현황 분석 (0) | 2010.10.09 |
[펌] Objective-C on Linux 2 (0) | 2010.10.02 |
[펌] Objective-C on Linux (0) | 2010.10.01 |
경의선 : 서울역 <=> 상암 DMC 출퇴근용 시간표 (0) | 2010.09.26 |