소개
원격(Remoting)은 C++에서 분산 (원격) 객체와 웹 서비스를 구현하기 위한 프레임웍이다. 다른 프로세스들과 심지어는 다른 머신에서 조차 서로 완벽하게 통신하기 위한 살아있는(동작하는) C++ 객체들을 위한 매커니즘을 구현한다. 개념적으로 원격은 Java RMI, .NET Remoting 또는 WCF와 더 적은 범위로 CORBA 또는 DCOM과 매우 유사하다. 원격의 가능한 사용 시나리오는 고수준 상호 통신(interprocess communication), 분산(또는 클라이언트-서버) 어플리케이션, 또는 다른 어플리케이션에 SOAP 웹 서비스를 제공하는 서버 어플리케이션들(자바 또는 .NET으로 만들어진 어플리케이션 처럼 어플리케이션이 원격으로 만들어 지지 않아도) 이다. 원격은 또한 XSDGen 코드 생성기의 도움으로 3rd party SOAP 1.1과 1.2 웹 서비스를 호출하기 위해 사용될 수 있다.
원격은 가능하면 간단하게 원격 객체들을 구현하도록 해준다. 학습하기 위해 특별한 인터페이스 정의 언어는 없다. 기본 아이디어는 보통의 C++ 클래스를 취하고, 네트웍상에 이용가능한 이 클래스의 객체를 만들기 위해 필요한 코드를 생성하는 코드 생성기를 구동하는 것이다.
원격은 코드 생성기에 많이 의존한다. 원격 코드 생성기 도구(RemoteGenNG)는 원격의 중심 파트이다. 원격 core 라이브러리는 원격 객체 등록과 검색(객체 요청 브로커(Object Request Broker, ORB) 기능으로 알려진)과 같은 기능을 제공한다.
이미 언급하였듯이, 원격의 뒤에 기본적인 아이디어는 어떤 서비스("서비스 클래스")를 구현하는 보통의 C++ 클래스를 취한다. 그리고 특별한 주석으로 주석을 단다. 이러한 주석들은 일반적으로 생성할 코드가 무엇인지 코드 생성기(RemoteGenNG)에 알려준다. 이러한 주석에 기반하여, 코드 생성기는 클라이언트 stub(proxy)을 위한 클래스, 서버 stub(skeleton), 인터페이스 클래스, 그리고 파라미터와 리턴 값을 위한 직렬화기와 반직렬화기(deserializer), 다양한 유틸리티 클래스를 제공하는 것들을 위한 클래스들을 만든다.
원격 NG vs. "고전" 원격
이용가능한 원격 프레임웍에는 사실 2가지 버전이 있다. 오리지널("고전") 원격 프레임웍과 새로운 원격 NG("Next Generation") 프레임웍. 원격 NG는 많은 개선된 설계를 갖고, cleaner API와 이벤트와 개선된 직렬화와 같은 추가적인 기능을 제공한다.
그러나, 원격 NG는 고전의 원격과는 완벽히 하위 호환되지는 않는다. 기존의 어플리케이션을 고전의 원격으로 부터 원격 NG로 업그레이드 하는 것은 소스 코드에 몇몇 마이너 수정을 필요로 한다. 원격 NG는 Applied Informatics Univeral Plug and Play 프레임웍을 위해 사용되어지는 첫번째 이다. 그리고, UPnp SOAP와 GENA 전송은 얼마 동안 이용할 수 있는 유일한 전송 수단이었다. 같은 어플리케이션에서 UPnP(원격 NG에 의존적인)와 고전 원격 모두에서 사용가능하도록 만들기 위해서, 원격 NG 프레임웍은 RemotingNG 네임스피이스를 사용한다. 그러므로, 같은 어플리케이션에서 고전의 원격 프레임웍과 공존하는 것이 가능하다.
어떻게 원격이 동작하는가
첫번째 예제로, 우리는 매우 단순한 "Time Service"를 구현하는 다음의 C++ 개체를 살펴본다. CurrentTime() 멤버 함수는 간단하게 std::string 객체로 현재의 날짜와 시간을 리턴한다.
class TimeService
{
public:
TimeService();
~TimeService();
std::string currentTime() const;
};
우리는 리턴 값으로 좀 더 적절한 Poco::DateTime 또는 Poco::Timestamp 클래스를 또한 사용할 수도 있다. 그러나, 이러한 것들을 좀더 단순하게 유지시키기 위해서, 우리는 지금 std::string을 사용할 것이다. 클래스의 실제 구현은 지금을 위한 관심사가 아니다(?). 원격 객체로써 가능한 클래스를 만들기 위해서 또는 다른 말로 네트웍상의 이 클래스의 인스턴스의 currentTime() 멤버 함수를 호출하는것이 가능하도록 만들기 위해서, 우리는 특별한 "원격" 속성을 갖는 클래스를 첫번째 annotate해야 한다.
//@ remote
class TimeService
{
public:
TimeService();
~TimeService();
std::string currentTime() const;
};
한번 이 주석이 추가되면, 원격 코드 생성기 (RemoteGenNG)는 클래스를 정의하는 헤더 파일을 파싱해서 클래스를 처리하기 위해 그리고 네트웍상에 이용간으한 이 클래스의 인스턴스를 만드는 필요한 코드를 생성하기 위해서 호출되어질 수 있다.
인터페이스 클래스
다른 클래스 사이에, 코드 생성기가 생성하는 중요한 클래스는 인터페이스 클래스이다. 인터페이스 클래스는 서비스 클래스(오로지 대문자 I 문자가 앞에 추가된)와 같은 이름을 갖는다. 그러므로, TimeService 클래스를 위한 인터페이스 클래스는 ITimeService라는 이름을 갖게 될 것이다. 인터페이스 클래스는 remote속성을 갖는 주석을 갖는(?) 서비스 클래스의 모든 메소드를 갖게 될 것이다. 또는 만약 클래스가 remote 속성을 갖는 모든 public 메소드.(?) 인터페이스 클래스에서, 이러한 메소드는 순수 가상 (함수)가 될 것이다 인터페이스 클래스는 원격 프레임웍에서 내부적으로 사용되는 것과 같은 몇몇 다른 메소드를 갖는다.
우리의 예제 클래스의 경우, 생성된 인터페이스 클래스는 다음과 같다:
class ITimeService: public virtual Poco::RefCountedObject
{
public:
typedef Poco::AutoPtr<ITimeService> Ptr;
ITimeService();
virtual ~ITimeService();
virtual std::string currentTime() const = 0;
static const Poco::RemotingNG::Identifiable::TypeId& remoting__typeId();
};
원격에서 투명하게 진짜 위치를 활성화시키는 인터페이스 클래스이다. 서비스 오브젝트의 멤버 함수가 클라이언트에 의해 호출될 때, 클라이언트는 항상 이 인터페이스 클래스를 사용한다. 인터페이스 클래스는 원격 프레임웍에서 중요한 최적화를 활성화 한다. 코드 생성기는 인터페이스 클래스를 구현하는 2가지 서브클래스를 생성한다. 첫번째 것은 프록시(Proxy)이고, 대개 네트웍 통신을 호출하는 진짜 원격 호출을 구현하기 위해 사용한다. 두번째 것은 RemoteObject이며, 서비스 오브젝트로 지역적으로 모든 멤버 함수 호출을 간단하게 포워드 한다. 원격 오브젝트가 오브젝트 요청 브로커(Object Request Broker, ORB)로 부터 획득될 때, ORB는 첫번째로 실제 서비스 오브젝트가 지역의 ORB에 알려진 것인지 확인한다. 만약 그렇다면, ORB가 RemoteObject 클래스의 인스턴스를 리턴한다. 만약 지역의 ORB가 서비스 오브젝트를 알지 못한다면, 이것은 메소드 호출의 수신된 응답과 같은 네트웍상의 생성과 요청을 보내는 것을 취하는 Proxy를 리턴한다(?).
어떻게 클라이언트가 어떤 서비스를 위한 RemoteObject 또는 Proxy로를 획득 할까? 가장 간단한 방법은 코드 생성기에 의해서 생성되는 ClientHelper 클래스를 사용하는 것이다.
std::string uri("http://server:8080/soap/TimeService/TheTimeService");
ITimeService::Ptr pTimeService = TimeServiceClientHelper::find(uri);
std::string time = pTimeService->currentTime();
원격 객체의 식별 및 찾기
인터페이스를 얻기 위해, 원격 객체의 위치는 알려져야 한다. 원격 객체의 위치는 URI(Uniform Resource Identifier)로 명시된다. 원격 객체, 원격 객체(실제 서비스 객체)가 상주하는 서버의 네트워크 주소, 직렬화를 위해 사용하는 프로토콜, 특정의 객체를 식별하는 서비스 객체와 객체 식별자이 클래스 명과 통신하기 위해 사용되는 네트워크 프로토콜로 이루어진다. 특정의 원격 객체를 위한 URI의 모드 부분이 알려지면, 스크래치로 부터 URI를 생성하기 매우 쉽다. 대개 URI는 구성 파일 또는 레지스트리를 구현하는 다른 원격 객체로부터 또는 URI로 명명된 사람이 읽기 쉬운 형태로 명명된 서비스로 부터 얻어질 것이다. 이러한 서비스는 원격의 일부가 아니지만, 스크래치로부터 쉽게 만들어 질 수 있다.
또한 원격 객체를 위한 URI는 서버로 부터 획득 할 수 있다. 서버가 ORB와 함게 서비스 객체를 등록할 때, ORB는 이 객체를 위한 URI를 생성하고 리턴한다.
어떻게 원격 메소드 호출이 작동하는가?
그림 1은 원격 메소드 호출 동안에 무엇이 발생하는지를 보여준다.
그림 1: 어떻게 원격 메소드 호출이 동작하는가?
클라이언트가 ORB로 부터 proxy 객체를 한번 획득하면, proxy를 통해서 원격 객체의 메소드를 호출 할 수 있다. 전송(transport)의 도움으로, 프록시는 메소드 호출을 원격 객체의 클래스 명, 객체 ID와 메소드 명을 포함하는 네트워크 요청으로 전환한다. 그 다음 이것은 또한 전송(transport)로 부터의 도움으로 모든 파라미터를 직렬화하고 서버로 그것들을 전송한다.
서버에서 리스너(Listener)는 클라이언트로 부터 요청(Request)를 받고, 스켈레톤(Skeleton)으로 이것을 전달한다. 스켈레톤(Skeleton)은 모든 파라미터들을 역직렬화하고, 차례로 실제 서비스 객체를 호출하는 원격 객체를 호출한다. 모든 출력 파라미터 뿐만아니라 리턴 값은 응답 메시지로써 클라이언트로 다시 그것들을 보내기 위해 직렬화한다.
클라이언트에서는 다시 프록시(Proxy)가 리턴 값, 출력 파라미터를 역직렬화하고, 호출자(caller)로 그것들을 리턴한다.
C++ 메소드 호출에 의해 서비스 객체로 포워드 된다. 그러므로, 네트워크와 마샬링 오버헤드는 이러한 호출에서 뒤얽히지 않는다.
전송(Transports)
.
.
.
TCP 전송
TCP 전송은 프록시당 클라이언트와 서버사이의 지속적인 TCP 연결을 사용한다. 첫번째 요청은 프록시를 통해 최대한 빨리 보내지고, 전송은 서버로 소켓 연결을 오픈한다. 파라미터와 리턴값들은 효율적인 바이너리 포맷으로 직렬화된다. 전송은 클라이언트와 서버사이의 바이트 오더 차이를 주의기페 다루고 머신 아키텍처와 클라이언트와 서버 시스템의 바이트 오더 차이가 나더라도 원격 객체를 호출하는 것이 가능하다. 직렬화 포맷은 원격 기반 어플리케이션사이에 빠른 통신이 요구되는 곳에서 TCP 전송은 좋은 선택이 되게 만들고 매우 효율적이다. TCP 전송은 보안 SSL/TLS 연결에서도 사용될 수 있다.
SOAP 전송
SOAP Transport는 HTTP(S) 레이어 위에 SOAP 1.1 또는 1.2를 사용한다. 이 전송은 원격 기반 C++ 어플리케이션에의 제공되는 원격 서비스가 다른 프레임웍을 사용하여 작성된 (또는 자바 또는 .NET과 같은 다른 프로그래밍 언어 또는 런타임 조차) 클라이언트에 의해 접근가능해야 한다면 좋은 선택이 된다. 이 전송은 3rd party 웹 서비스들에 의해 호출되기 위해서도 사용되어질 수 있다. MTOM(Message Transport Optimization Mechanism), HTTP 압축과 HTTP 기본과 (Di
RemoteGenNG에의해 생성된 클래스들
그림 2는 원격 코드 생성기가 생성한 클래스들을 보여준다. 모든 클래스들은 하나의 입력 클래스(원격 서비스를 구현하는 서비스 클래스)로부터 생성된다. 그림에 보여지지 않는 것은 아규먼트나 리턴값으로 사용되는 모든 사용자 정의 클래스를 위해 생성되는 직렬화, 역직렬화 클래스이다.
그림 2: RemoteGenNG에 의해 생성된 클래스들
IService
ServiceProxt
ServiceRemoteObject
ServiceSkeleton
ServiceProxyFactory
ServiceEventSubscriber
ServiceEventDispatcher
ServiceServerHelper
SerivceClientHelper
원격이 하는것과 하지 않는 것
.
.
.