미들웨어의 실제 구성, 즉 분산 시스템 또는 애플리케이션의 전체 구성과 무관하게 확대해 본다. 특히, 미들웨어 구성에 자주 적용되는 두 가지 중요한 디자인 패턴 유형이 있다. 래퍼와 인터셉트다. 서로 다른 문제를 다루지만 개방성(Openess)을 목표로 한다. 그러나 개방성은 런타임 시 미들웨어를 구성할 수 있을 때 달성된다는 것을 궁극적으로 주장할 수 있다.
Wrappers
기존 구성 요소에서 분산 시스템을 구축할 때 우리는 즉시 근본적인 문제에 부딪힌다. 레거시 구성 요소에서 제공하는 인터페이스가 모든 응용프로그램에 적합하지 않을 가능성이 높다. 통신 촉진자로서 미들웨어를 통해 엔터프라이즈 애플리케이션 통합을 설정하는 방법에 대해 논의했지만 결국 구성 요소가 기본 인터페이스를 통해 액세스 될 수 있다고 가정했다.
래퍼나 어댑터는 클라이언트 응용 프로그램에서 사용할 수 있는 인터페이스를 제공하는 특수 구성 요소이며, 이 중 기능은 구성 요소에서 사용할 수 있는 기능으로 변환된다. 본질적으로 호환되지 않는 인터페이스의 문제를 해결한다.
원래 객체 지향 프로그래밍의 맥락에서 좁게 정의되었지만 분산 시스템에서의 맥락에서 래퍼는 단순한 인터페이스 변환기 그 이상이다. 예를 들어, 객체 어댑터는 응용 프로그램이 원격 개체를 호출할 수 있도록 하는 구성 요소이지만 해당 개체는 관계형 데이터베이스의 테이블에서 작동하는 라이브러리 기능의 조합으로 구현되어 있을 수 있다.
또 다른 예로 Amazon의 S3 스토리지 서비스를 다시 고려해본다. 이제 두 가지 유형의 인터페이스를 사용할 수 있다. 하나는 RESTful 아키텍처를 준수하고 다른 하나는 보다 전통적인 접근 방식을 따른다. RESTful 인터페이스의 경우 클라이언트는 HTTP 프로토콜을 사용하여 기본적으로 현재 실제 스토리지 서비스에 대한 어댑터 역할을 하는 기존 웹 서버와 통신하고 수신 요청을 부분적으로 분석한 다음 S3 내부의 특수 서버로 전달합니다.
래퍼는 기존 구성 요소로 시스템을 확장하는 데 항상 중요한 역할을 했다. 개방성을 확보하는 데 중요한 확장성은 필요에 따라 래퍼를 추가하여 해결했다. 즉, 응용 프로그램 A가 응용 프로그램 B에 필요한 데이터를 관리하는 경우 한 가지 접근 방식은 B에 특정한 래퍼를 개발하여 A의 데이터에 액세스 할 수 있도록 하는 것이다. 분명히 이 접근 방식은 확장성이 좋지 않다. N개의 애플리케이션을 사용하면 이론적으로 N X (N-1) = O(N^2) 래퍼를 개발해야 한다.
다시, 래퍼 수를 줄이는 것은 일반적으로 미들웨어를 통해 수행된다. 이를 수행하는 한 가지 방법은 서로 다른 응용 프로그램 간의 모든 액세스를 처리하는 논리적으로 중앙 집중식 구성 요소인 소위 브로커를 구현하는 것이다. 자주 사용되는 유형은 메시지 브로커다. 메시지 브로커의 경우, 애플리케이션은 필요한 정보가 담겨있는 요청을 브로커에게 보내기만 하면 된다. 모든 관련 애플리케이션에 대한 지식을 갖고 있는 브로커는 적절한 애플리케이션에 컨택하고, 가능한 결합 및 응답을 변환하고 그리고 결과를 초기 애플리케이션에 반환한다. 원칙적으로 브로커는 각 애플리케이션에 단일 인터페이스를 제공하므로 O(N^2) 대신 최대 2N = O(N) 래퍼가 필요하다.
Interceptors
개념적으로 인터셉터는 일반적인 제어 흐름을 깨고 다른 (애플리케이션 별) 코드가 실행되게 하는 소프프웨어 구성에 불과하다. 인터셉터는 애플리케이션의 특정 요구 사항에 미들웨어를 적용하기 위한 기본 수단이다. 따라서, 미들웨어를 개방하는 데 중요한 역할을 한다. 인터셉터를 일반화하려면 상당한 구현 노력이 필요할 수 있다. 그리고 그러한 경우에 제한된 적용 가능성과 단순성보다 일반성을 선호해야 하는지 여부는 불분명하다. 또한, 대부분의 경우 차단 기능이 제한되어 있으면 소프트웨어와 분산 시스템 전체의 관리가 향상된다.
문제를 구체화하기 위해 많은 객체 기반 분산 시스템에서 지원되는 인터셉션을 고려한다. 기본 아이디어는 간단하다. : 객체 A는 객체 B에 속한 메서드를 호출할 수 있지만 후자는 A와 다른 시스템에 상주한다. 이러한 원격 객체 호출은 3단계로 수행된다.
- 객체 A는 객체 B가 제공하는 인터페이스와 정확히 동일한 로컬 인터페이스를 제공한다. A는 해당 인터페이스에서 사용 가능한 메소드를 호출한다.
- A에 의한 호출은 A가 있는 머신에서 미들웨어가 제공하는 일반 객체 호출 인터페이스를 통해 가능한 일반 객체 호출로 변환된다.
- 마지막으로 일반 객체 호출은 A의 로컬 운영 체제에서 제공하는 전송 수준 네트워크 인터페이스를 통해 전송되는 메시지로 변환된다.
첫 번째 단계 후에 B.doit(val) 호출은 B의 메서드 및 호출과 함께 수행되는 매개 변수에 대한 참조를 사용하여 invoke(B, &doit, val)와 같은 일반 호출로 변환된다. 이제 객체 B가 복제되었다고 상상한다. 이 경우 각 복제본이 실제로 호출되어야 한다. 이것은 차단이 도움이 될 수 있는 분명한 지점이다. 요청 수준 인터셉터가 하는 일은 단순히 각 복제본에 대해 invoke(B, &doit, val)를 호출하는 것이다. 이 모든 것의 장점은 객체 A가 B의 복제를 인식할 필요가 없지만 객체 미들웨어가 이 복제된 호출을 처리하는 특별한 구성 요소를 가질 필요가 없다는 것이다. 미들웨어에 추가될 수 있는 요청 수준 인터셉터만 B의 복제에 대해 알아야 한다.
마지막으로, 원격 객체에 대한 호출은 네트워크를 통해 보내야 한다. 실제로, 이것은 운영 체제에서 제공하는 메시징 인터페이스를 호출해야 함을 의미한다. 해당 수준에서 메시지 수준 인터셉터는 호출을 대상 개체로 전송하는 데 도움이 될 수 있습니다. 예를 들어, 매개변수 val이 실제로 방대한 데이터 배열에 해당한다고 상상해 본다. 이 경우 데이터를 더 작은 부분으로 조각화하여 대상에서 다시 조합하도록 하는 것이 현명할 수 있다. 이러한 단편화는 성능이나 안정성을 향상시킬 수 있다. 다시 말해서, 미들웨어는 이 조각화를 인식할 필요가 없다. 하위 수준 인터셉터는 로컬 운영 체제와 나머지 통신을 투명하게 처리한다.
Modifiable middleware
래퍼와 인터셉터가 제공하는 것은 미들웨어를 확장하고 조정하는 수단이다. 적응의 필요성은 분산 응용 프로그램이 실행되는 환경이 지속적으로 변화한다는 사실에서 비롯됩니다. 변경 사항에는 이동성, 네트워크 서비스 품질의 큰 차이, 하드웨어 오류, 배터리 소모 등으로 인한 변경 사항이 포함된다. 애플리케이션이 변경 사항에 대응하도록 만드는 대신 이 작업은 미들웨어에 배치된다. 더욱이 분산 시스템의 크기가 커짐에 따라 일시적으로 시스템을 종료하여 해당 부분을 변경할 수 있는 경우는 거의 없다. 필요한 것은 즉석에서 변경할 수 있어야 한다.
환경의 이러한 강력한 영향으로 인해 많은 미들웨어 설계자가 적응형 소프트웨어 구성을 고려하게 되었다. 우리는 수정 가능한 미들웨어에 대해 미들웨어가 적응할 필요가 있을 뿐만 아니라 중단 없이 의도적으로 수정할 수 있어야 한다고 표현한다. 이러한 맥락에서 인터셉터는 표준 제어 흐름을 조정하는 수단을 제공하는 것으로 생각할 수 있다. 런타임에 소프트웨어 구성 요소를 교체하는 것은 시스템 수정의 한 예다. 그리고 실제로 수정 가능한 미들웨어에 대한 가장 인기 있는 접근 방식 중 하나는 구성 요소에서 미들웨어를 동적으로 구성하는 것이다.
구성 요소 기반 (component-based) 디자인은 구성을 통해 수정 가능성을 지원하는 데 중점을 둔다. 시스템은 디자인 타임에 정적으로 구성되거나 런타임에 동적으로 구성될 수 있다. 후자는 프로그래밍 언어 환경에서 성공적으로 적용된 기술인 후기 바인딩에 대한 지원이 필요하지만 모듈을 마음대로 로드 및 언로드할 수 있는 운영체제에도 지원해야 한다. 런타임 동안 구성 요소의 최상의 구현을 자동으로 선택하기 위한 연구가 현재 진행 중이지만, 다시 말해, 분산 시스템의 경우 프로세스가 복잡하다. 특히 한 구성 요소의 교체가 다른 구성 요소에 미치는 영향을 정확히 알아야 한다는 점을 고려할 때 더욱 그렇다. 많은 경우에 구성 요소는 생각보다 덜 독립적이다.
결론은 미들웨어를 구성하는 소프트웨어에 대한 동적 변경 사항을 수용하려면 런타임에 구성 요소를 로드 및 언로드할 수 있는 최소한의 기본 지원이 필요하다는 것이다. 또한, 각 구성 요소에 대해 제공하는 인터페이스의 명시적 사양과 필요한 인터페이스가 필요하다. 구성 요소에 대한 호출 간에 상태가 유지되는 경우 추가 특수 조치가 필요하다. 대체로 미들웨어를 수정할 수 있도록 구성하려면 매우 특별한 주의가 필요하다.