COM, ATL

분산객체 시스템(COM,COm+,DCOM,MTS) 에 대한 개념-2

디버그정 2008. 7. 28. 13:59
분산객체 시스템(COM,COm+,DCOM,MTS) 에 대한 개념-2 MTS

2004/12/14 12:36

복사 http://blog.naver.com/saga111/120008481356

트랜잭션

기존의 MTS 환경에서와 마찬가지로 COM+ 환경에서도 컴포넌트 수준에서 분산 트랜잭션에 쉽게 참여할 수 있다. MTS에서 분산 트랜잭션 처리를 마이크로소프트 분산 트랜잭션 조정자(DTC)를 통해 처리했듯이 COM+ 환경 역시 동일한 방법으로 분산 트랜잭션을 처리한다. 마이크로소프트는 트랜잭션을 처리하기 위해 객체 지향 개념의 two-phase 커밋을 지원하는 OLE 트랜잭션을 설계하였으며 MS DTC로 하여금 수행하게 만들었다. 최초의 MS DTC는 마이크로소프트 SQL 서버의 한 제품으로 공급됐으나 현재는 다양한 트랜잭션 처리를 위한 기본 시스템으로 윈도우 2000에 통합시켜 공급하고 있다.

보통 트랜잭션은 중요한 작업(트랜잭션을 사용해 안정적으로 처리될 필요가 있는)시 시작되며, 애플리케이션은 트랜잭션을 MS DTC와 같은 트랜잭션 관리자에게 전달함으로써 실제 트랜잭션을 시작한다. MS DTC는 트랜잭션에 참여하고 있는 다양한 리소스 관리자에게 각각의 트랜잭션을 수행하게 하며 성공여부에 따라 전체적인 커밋을 할지 롤백을 할지를 결정한다. 여기서 언급하는 리소스 관리자는 SQL 서버와 같이 OLE 트랜잭션 스펙을 지원하는 서비스 전체를 의미한다. 전형적으로 트랜잭션은 데이터베이스 액세스에 주로 사용되었지만 절대 트랜잭션 처리가 데이터베이스에 제한되어 있지는 않다. OLE 트랜잭션을 지원하는 어떠한 서비스도 MS DTC 트랜잭션에 참여할 수 있다. 단적인 예로 MSMQ 역시 OLE 트랜잭션을 지원하므로 분산 트랜잭션에 참여 가능하다.

COM+는 기본적으로 두 개의 리소스 분배기(Dispenser)인 OD BC 드라이버 매니저와 공유 프로퍼티 매니저를 제공한다. ODBC 드라이버 매니저는 COM+ 컴포넌트를 위해 데이터베이스 연결에 대한 풀링을 제공한다. 데이터베이스 연결 풀링에 관한 자세한 내용은 ‘MTS 프로그래밍의 다섯 가지 규칙, 서버의 자원관리’ 부분을 참조하기 바란다. COM+ SDK를 사용하면 기본적인 리소스 분배기 외에 추가로 필요한 분배기를 제작할 수 있다.

클라이언트 애플리케이션 입장에서 바라보면 리소스 관리자는 전체 트랜잭션에 참여하는 하나의 구성요소 이지만, 리소스 관리자 역시 그 자체가 트랜잭션 관리자로서의 역할을 수행해야 한다. 클라이언트 애플리케이션에서 트랜잭션에 대한 커밋 또는 롤백을 지시해야 트랜잭션은 종료된다. 만약 롤백이 요구됐다면 MS DTC는 트랜잭션에 참여하고 있는 모든 리소스 관리자에게 트랜잭션에 참여하면서 진행했던 모든 작업을 롤백하라고 명령할 것이다. 각각의 리소스 관리자는 정확한 롤백 작업을 수행할 책임이 있다. 만약 클라이언트가 커밋 또는 롤백하기 전에 오류로 죽는다면 MS DTC는 자동으로 전체 트랜잭션을 롤백한다.

클라이언트가 트랜잭션이 커밋되도록 요청한다면 MS DTC는 트랜잭션 작업 중에 발생한 모든 변화를 커밋하기 위해 two-phase 커밋 프로토콜을 실행한다. 첫 단계로 MS DTC는 트랜잭션에 참여하고 있는 모든 리소스 관리자에게 커밋 작업에 동의하는 지를 결정하기 위해 질문을 던진다. 질문에 답하지 않거나 커밋에 동참할 수 없다라고 응답하는 리소스 관리자가 하나라도 있으면, MS DTC는 모든 리소스 관리자에게 트랜잭션을 취소하라고 명령하며 각 리소스 매니저는 롤백 작업을 진행할 것이다.

롤기반 보안

COM+ 보안 모델은 윈도우 2000 보안 모델을 기본으로 하지만, 보다 쉬운 사용을 위해 두 가지 형태, 선언적(declarative) 보안과 프로그래밍 가능한 보안을 지원한다. COM+ 보안 메커니즘의 핵심은 롤(role)의 개념을 정확히 이해하는데 있다. 롤은 대다수 CO M+ 객체에 의해 사용되어지며 매우 유연하고, 선언적인 보안 모델의 중심에 서 있으며, 논리적 사용자 그룹(윈도우 2000의 사용자 그룹과 유사한 개념)을 구분하는 이름이다. COM+ 객체가 시스템에 설치되어 사용될 때, 관리자는 롤을 생성한 후 특별한 사용자 또는 사용자 그룹을 해당 롤에 포함시킨다. 그 다음 관리자는 설치된 컴포넌트에 대해 어떠한 롤을 사용할 지를 결정한다. 기본적으로 컴포넌트 수준에서 롤을 결정할 수 있으며, 또한 컴포넌트가 제공하는 인터페이스 별로 롤을 지정할 수도 있다. 이러한 선언적 방법은 컴포넌트 소스 코드를 전혀 수정하지 않고 컴포넌트를 설치하고 운영하는 관리자 수준에서 아주 쉽게 다양한 보안을 제공할 수 있다.

때때로 선언적 보안만으로 처리하기 힘든 경우가 발생한다. 예를 들어 나이가 20살 이상인 회원에 대해서만 해당 메쏘드를 실행시키길 원한다면 위의 선언적 보안으로 처리하기는 불가능하다. 이때는 COM+ 객체를 개발하는 단계(소스 코드 작성 단계)에서 프로그램 로직으로 구현해야 한다. COM+ 보안 모델은 프로그램 상에서도 쉽게 롤을 사용할 수 있는 여러 API를 제공한다.

결론

‘휴’ 라는 한숨이 절로 나온다. 나날이 변화하는 환경에 과연 적응할 수 있을까? 오래전 골방에서 C언어를 사용해 DOS 환경에서 수행되는 애플리케이션을 만들던 시절, 아니 좀더 시간이 지나 피땀 흘려 C++를 익힌후 볼랜드의 OWL(Object Windows Library)을 사용해 윈도우 3.1용 애플리케이션을 만들던 시절이 그리워진다. 그 시절의 마소를 펼쳐보았다. 멀티쓰레딩, 트랜잭션, 분산 처리라는 말은 아무리 눈을 씻고 찾아봐도 볼 수 없었다. 물론 그때의 마소 토픽은 그 당시 컴퓨팅 환경에서 최고의 이슈였겠지만 지금 뒤돌아보면 너무도 간단하고 명료한 문제를 가지고 고민했었구나라는 생각이 든다. 과연 수년 후 현재의 분산 환경을 뒤돌아 볼 때 이러한 생각이 들까? 필자는 향후에도 현재의 고민을 계속해야만 할 것 같은 불길(?)한 예감이 든다. 분산 환경은 단지 컴퓨터 공학에 국한된 문제가 아니라, 인간이 살아가는 방식에 관한 시스템적 접근에 대한 문제이기 때문이다.

DCOM, MTS, MSMQ 등의 출현으로 과거에 비해 분산 환경을 꾸미기가 좀더 편리해졌다는 것 외에 달라진 건 없다. 아마 미래에는 보다 쉽고 강력한 도구가 만들어져 현재보다 좀 더 편하게 된다는 것 외에 쉬워진건 하나도 없을 것이다. 우리의 삶의 방식 역시 지금보다 훨씬 더 복잡해질 것이고, 이들을 시스템으로 표현하기 위해서는 지금보다 더 많은 고민을 해야할지도 모른다.

필자 역시 다음 프로젝트는 COM+ 기반의 검색 엔진 솔루션을 개발할 계획이다. 벌써부터 데이터 검색이라는 작업을 어떻게 효율적으로 여러 컴퓨터에 분산시키며, 다른 트랜잭션과 연동시킬 수 있을까라는 생각으로 머리가 복잡하다.

필자 연락처 : khkim97@shinbiro.com
정리 : 강경수 elegy@sbmedia.co.kr


--------------------------------------------------------------------------------

MTS 프로그래밍의 다섯 가지 규칙

MTS에서 비롯된 COM 프로그래밍 모델의 변화는 크게 5가지로 나눠 생각할 수 있다. MTS 컴포넌트를 사용하는 클라이언트에는 큰 변화가 없는 대신 서버에 제한된 변화이며, MTS의 성능을 최대화시키기 위한 방법으로 MTS 애플리케이션 개발자는 반드시 이 규칙을 따라야 한다.

서버는 SetComplete를 최대한 많이 호출해야 한다. 이것은 서버가 더 이상 유지해야 할 상태를 갖지 않는 것을 의미한다. 이렇게 함으로써 많은 수의 클라이언트에 대해 효율적인 서비스를 제공할 수 있다.
클라이언트는 되도록 COM 개체의 인터페이스를 빨리 얻고 지속적으로 유지해 줘야 한다. 비록 당장 COM 개체를 사용하지 않더라도 먼저 인터페이스를 얻어야 한다. 일반적으로 객체에 대한 참조를 얻는 것은 많은 리소스를 요구하지만 MTS를 이용하면 상태를 갖지 않는 객체에 대한 참조를 유지하는 데는 전혀 자원이 필요치 않다.
서버가 사용하는 데이터베이스 연결과 같은 자원은 최대한 늦게 할당받는다. 그리고 사용 후 신속히 반납해야 한다. MTS는 이러한 자원을 얻는 과정을 좀더 효율적으로 처리하게 해준다.
서버는 롤과 선언적 보안을 사용해야 한다. 기존의 개인 정보와 ACL을 사용한 보안 정책은 확장성이 좋지 않다.
서버는 적절한 시기면 언제나 트랜잭션을 사용해야 한다.
SetComplete를 최대한 자주 호출하라

클라이언트가 바라보는 MTS 컴포넌트는 단지 일반적인 COM 컴포넌트와 차이가 없지만 컴포넌트 서버는 많은 차이를 보인다. <그림 1>에서 보는 것처럼 MTS 컴포넌트는 클라이언트와 직접 연결되어 있지 않고 MTS 실행부를 통과한다. 클라이언트가 MTS 컴포넌트를 생성하기 위해 CoCreateInstance와 같은 API를 호출하면 MTS 실행부는 이를 가로채 특별한 부가 작업을 수행한다. 이들 부가 작업 중 가장 중요한 것은 컨텍스트 객체를 생성한다는 것이다. 이 컨텍스트 객체를 통해 MTS 컴포넌트와 MTS 실행부는 통신을 하게 된다. 컨텍스트 객체가 제공하는 IObjectContext 인터페이스가 제공하는 메쏘드 중에 SetComplete, SetAbort가 있으며 호출되면 MTS 실행부는 중요한 몇 가지 작업을 수행한다. 트랜잭션 수행시 이 메쏘드가 호출되면 트랜잭션을 커밋하거나 롤백한다. 트랜잭션을 사용하지 않을 경우도 이 메쏘드를 호출할 수 있으며, 그 경우 MTS 실행부에게 더 이상 MTS 객체는 유지할 상태가 없다는 것을 알려준다.

객체가 더 이상 유지할 상태가 없다는 것은 더 이상 객체가 존재할 필요가 없다는 의미다. 객체가 다시 필요하게 되면 새로운 인스턴스를 생성한다. 객체가 필요 없을 시 제거하면 그만큼 서버 메모리 사용은 줄어든다. 이렇게 객체를 삭제했다가 필요시 다시 생성하는 일련의 작업은 클라이언트가 전혀 알아채지 못하게 MTS 실행부가 수행한다. 이러한 동작이 가능하도록 MTS 실행부는 <그림 2>와 같이 COM 객체를 랩퍼(wrapper) 객체로 감싸고 있다, SetComplete 호출시 MTS는 <그림 3>과 같이 실제 MTS 객체는 릴리즈하고 다만 랩퍼만 유지한다. 클라이언트 입장에서 보면 실제 MTS 객체를 참조하는 것이 아니라 랩퍼를 참조하고 있는 상태이므로 어떠한 변화도 감지하지 못한다. 클라이언트가 다시 이 객체의 메쏘드를 호출하면 MTS는 객체를 다시 생성하기 위해 클래스 팩토리를 사용하게 된다. 객체가 생성되면 클라이언트의 호출을 전달해 준다. 이러한 일련의 동작을 JIT(Just-In-Time) 실행이라고 하며 <그림 2>의 상태로 복원시켜 준다. 객체는 어떠한 상태도 유지하고 있을 필요가 없으므로 새롭게 생성된 객체는 그 전의 객체와 구분할 수 없다. 따라서 가능한 자주 SetComplete 함수를 호출해 MTS에서 더 이상 필요 없는 객체를 제공하게 해야한다.

인터페이스 포인터를 빨리 얻고 유지하라

일반적인 COM, DCOM 프로그램에서 클라이언트가 지금 당장 필요 없는 객체를 생성하고 이에 대한 인터페이스 포인터를 유지하고 있는 것은 그리 바람직한 상황은 아니다. 왜냐면 사용되지 않는 객체를 위해 필요 없는 메모리를 더 쓰고 있기 때문이다. 그러나 MTS 환경에서는 상태가 없는 객체는 즉시 메모리에서 삭제되므로 굳이 클라이언트가 객체에 대한 참조를 릴리즈할 필요가 없다.

MTS는 클라이언트에서 보이지 않기 때문에 일반적인 COM이라고 생각할 수도 있지만, 간혹 클라이언트가 MTS 객체의 메쏘드를 호출할 때 주의해야할 사항이 있다. 예를 들어 프로퍼티에 특정 값을 지정하고 SetComplete가 포함돼있는 메쏘드를 호출한 후 프로퍼티 값을 읽는다고 가정하자. SetComplete를 호출하므로 객체는 메모리에서 제거되어 프로퍼티에 지정한 값 역시 사라질 것이다. 이러한 문제를 해결할 수 있는 유일한 방법은 클라이언트가 컴포넌트에 대해 자세히 아는 것이다. 어떤 메쏘드가 SetComplete를 호출하는지 SetAbort를 호출하는지에 대한 사전 지식이 있어야 한다.

서버의 자원 관리

일반적으로 데이터베이스에 대한 ODBC 연결 등의 서버 자원을 얻어야하는 경우 많은 시간이 소모된다. 그러므로 보통 COM 개발자는 이와 같은 자원을 가능하면 초기에 얻고 객체가 종료될 때 반환한다. 그러나 이러한 방식은 속도는 빨라질지 몰라도 컴포넌트를 사용하는 클라이언트 숫자가 늘어난다면, ODBC 연결은 유한한 시스템 자원이므로 얻지 못하는 클라이언트가 발생할 것이다. 그렇다고 필요시 연결하고 사용 즉시 반납한다면 그만큼 수행 속도는 많이 느려질 것이다.

MTS 자원 분배기는 이러한 문제를 효율적으로 해결해준다. MTS에서 현재 가장 중요한 자원 분배기는 ODBC 연결 관리기로 사용 가능한 ODBC 연결 풀(pool)을 관리해준다. MTS 객체가 데이터베이스 연결을 요청하면 자원 분배기는 풀에 있는 연결을 제공해 주며, 객체가 연결을 해제하면 다시 자원 분배기는 그 연결을 풀에 돌려준다.

실제 데이터베이스 연결에 관한 자원은 생성되거나 삭제되지 않으므로 처리 속도는 훨씬 빨라지게 된다. 따라서 MTS 컴포넌트를 제작할 때는 이러한 특성을 고려해서 메쏘드 호출시 연결을 수행하고 데이터를 처리한 뒤 바로 연결을 반납하게 만든다. 이렇게 함으로써 한정된 자원인 ODBC 연결을 여러 클라이언트가 공유하므로 많은 클라이언트 처리가 가능해진다.

서버 롤과 선언적 보안 사용

클라이언트가 COM 객체에서 메쏘드를 호출할 때 그 객체는 현재 자신을 호출하는 클라이언트가 이 메쏘드를 실행할 권한이 있는지 확인해야 한다. 가장 일반적인 방법으로는 클라이언트 개인 정보를 확인하는 서버 쓰레드가 존재해서 클라이언트가 자원을 사용할 때 처리해주는 동작을 하는 것이다. 윈도우 NT 서버의 경우 자원의 ACL이 자동으로 체크되기 때문에 프로그래머가 이에 대한 처리를 해줄 필요가 없다.

그러나 이러한 접근 방법은 원하는 결과를 정확히 얻기가 어렵고 확장성이 좋지 않다. 그래서 MTS는 선언적 보안이라는 새로운 방법을 제공하고 있다. 선언적 보안은 롤이라는 개념에 기반을 두고 있으며, 롤은 단지 윈도우 NT 서버의 사용자, 그룹의 모임이며 유일한 이름을 가진다. MTS 관리자는 MTS 관리 툴을 이용해 각 사용자와 그룹이 어떤 롤에 속하는지 지정한다. 롤이 지정되면 관리자는 각 롤이 어떠한 MTS 컴포넌트를 사용할 수 있는지 지정할 수 있으며, 컴포넌트의 각 인터페이스에 대해 세부 권한을 지정할 수 있다.

이와 같이 일단 롤을 지정해주면 추가적인 프로그램 코드의 사용 없이 쉽게 보안 메커니즘을 구현할 수 있다. 물론 보다 복잡한 보안을 처리하기 위해 프로그램적 보안 방법 역시 가능하다. IObjectContext가 제공하는 IsCallerInRole 메쏘드를 사용해 호출자가 어떤 롤에 속하는지 알 수 있으므로 보다 세밀한 제어가 가능하다.

서버 트랜잭션 사용

MTS는 트랜잭션 처리 기능보다는 분산 컴포넌트의 효율적 수행 환경에 보다 초점이 맞춰져 있다고 많은 사람들이 생각하고 있다. 그래서 혹자는 ‘이름을 잘못 지었다’고 이야기하곤 한다. 그러나 트랜잭션 처리 역시 MTS의 중요한 기능이므로 완전한 이해를 통해 적재적소에 사용해야 한다.

하나의 데이터베이스에 대한 트랜잭션은 데이터베이스 자체로 충분히 처리가 가능하지만 둘 이상의 데이터베이스에 걸친 트랜잭션 처리 및 다른 애플리케이션이 포함된 트랜잭션 처리 작업은 쉽지 않다. MTS는 이러한 이질적 시스템에 대한 분산 트랜잭션을 지원하므로 아주 쉽게 처리할 수 있다. 또한 컴포넌트 모델과 트랜잭션 모델을 통합시켰으므로 클라이언트는 트랜잭션에 대한 어떠한 고려를 하지 않아도 된다.

컴포넌트를 제작할 때 트랜잭션에 대해 충분히 고려한다면 설계 단계에서부터 완전히 새롭게 구상해야 한다. 가장 활용도를 높게 하기 위해서는 각 컴포넌트를 분리된 업무 단위로 캡슐화할 필요가 있다. 이렇게 하면 각 컴포넌트 사이의 트랜잭션 연결을 쉽게 처리할 수 있다.