接受器-連接器設計模式(Acceptor-Connector)使分布式系統中的連接建立及服務初始化與一旦服務初始化后所執行的處理去耦合。這樣的去耦合通過三種組件來完成:acceptor、connector和service handler(服務處理器)。連接器主動地建立到遠地接受器組件的連接,并初始化服務處理器來處理在連接上交換的數據。同樣地,接受器被動地等待來自遠地連接器的連接請求,在這樣的請求到達時建立連接,并初始化服務處理器來處理在連接上交換的數據。隨后已初始化的服務處理器執行應用特有的處理,并通過連接器和接受器組件建立的連接來進行通信。
圖9-1 面向連接的應用級網關的物理體系
為演示接受器-連接器模式,考慮圖9-1中所示的多服務、應用級Gateway(網關)。通常,Gateway使分布式系統中的協作組件去耦合,并允許它們在無需互相直接依賴的情況下進行交互。圖9-1中的這個Gateway在不同的服務端點間路由數據,這些端點運行在用于監視和控制人造衛星群的遠地Peer(對端)上。每個Peer中的服務經由Gateway發送和接收若干類型的數據,比如狀態信息、大塊數據和命令。一般而言,Peer可以分布在局域網和廣域網中。
該Gateway是一個路由器,負責協調它的Peer之間的通信。從Gateway的角度來看,它為之路由數據的Peer服務僅由其應用級通信協議來進行區分,這些協議可能會使用不同的幀格式和有效負載類型。
Gateway在它的Peer之間使用面向連接的TCP/IP協議[11]來傳輸數據。在我們的示例網絡配置中,每個服務都與一個連接端點綁定在一起;該端點由IP主機地址和TCP端口號指定。端口號唯一地標識服務的類型。為每種類型的服務/端口維護單獨的連接增強了路由策略的靈活性,并提供了更為健壯的錯誤處理,如果網絡連接意外關閉的話。
在我們的分布式應用中,Gateway和Peer必須能改變它們的連接角色,以支持不同的使用情況。特別地,或是可以主動地發起連接,或是可以被動地等待連接請求。例如,在一種配置中,Gateway可以主動地發起到遠地Peer的連接,以便路由數據給它們。在另一種配置中,Gateway可以被動地接收來自Peer的連接請求,后者隨即經由Gateway路由數據給另外的Peer。同樣地,在一種使用情況下,Peer可以是主動的連接發起者,而在另一種使用情況下則是被動的連接接受者。
由于我們的分布式應用的天性,預先指定連接建立和服務初始化角色、并將它們硬編碼進Gateway和Peer組件的傳統設計太不靈活。這樣的設計過度地將連接建立、服務初始化和服務處理組件耦合在一起。這樣的緊耦合使得獨立于通信角色改變連接角色變得很困難。
分布式系統中利用面向連接協議來在服務端點間進行通信的客戶/服務器應用。
分布式應用常常含有復雜的代碼,執行連接建立和服務初始化。一般而言,分布式應用中在服務端點間交換的數據的處理極大地獨立于配置問題,比如(1)是哪一個端點發起連接,也就是,連接角色 vs. 通信角色,以及(2)連接管理協議 vs. 網絡編程API。下面對這些問題進行概述:
-
連接角色 vs.
通信角色:連接建立角色天然地不對稱,也就是,被動的服務端點進行等待,而主動的服務端點發起連接。但是,一旦連接建立,通信角色和連接角色可以是互不相關的。因而,數據可以任意的服從服務通信協議的方式在服務端點間進行傳輸。常用的通信協議包括點對點、請求-響應和單路流。
-
連接管理協議 vs.
網絡編程API
:不同的網絡編程接口,比如socket或TLI,提供不同的API來使用多種連接管理協議建立連接。但是,不管用于建立連接的協議是什么,數據可以使用統一的消息傳遞操作來在端點間進行傳輸,例如,send/recv調用。
一般而言,用于連接建立和服務初始化的策略變動的頻度要遠小于應用服務實現和通信協議。因而,使這些方面去耦合、以使它們能獨立地變化,對于開發和維護分布式應用來說是必要的。對于將連接及初始化協議與通信協議分離的問題,下面一些壓力會對解決方案產生影響:
- 應該容易增加新類型的服務、新的服務實現和新的通信協議,而又不影響現有的連接建立和服務初始化軟件。例如,可能有必要擴展Gateway,以與運行在IPX/SPX通信協議、而不是TCP/IP之上的目錄服務進行互操作。
- 應該有可能使下面的兩種角色去耦合:(1)連接角色,也就是,哪一個進程發起連接 vs. 接受連接,以及(2)通信角色,也就是,哪一個服務端點是客戶或服務器。通常,“客戶”和“服務器”之間的區分指的是通信角色,它們可以與連接角色不相關。例如,在發起到被動服務器的連接時,客戶常常扮演主動角色。但是,這些連接角色可以反轉過來。例如,扮演主動通信角色的客戶可以被動地等待另一個進程對其進行連接。9.2中的例子演示了后一種使用情況。
- 應該有可能編寫可以移植到許多OS平臺上的通信軟件,以最大化可用性和市場占有率。許多低級網絡編程API的語義只是有著表面的不同,而語法卻互不兼容,因而難以使用低級API,比如socket和TLI,來編寫可移植應用。
- 應該有可能將程序員與低級網絡編程API(像socket或TLI)類型安全性的缺乏屏蔽開來。例如,連接建立代碼應完全地與后續的數據傳輸代碼去耦合,以確保端點被正確地使用。沒有這種強去耦,服務可能會錯誤地在被動模式的傳輸端點工廠上讀寫數據,而后者僅應被用于接受連接。
- 應該有可能通過使用像異步連接建立這樣的OS特性來降低連接響應延遲。例如,有大量對端的應用可能需要異步、并發地建立許多連接。高效和可伸縮的連接建立對于運行在高響應延遲的WAN上的應用來說特別重要。
- 應該可以盡可能多地復用通用的連接建立和服務初始化軟件,以有效利用先前的開發成果。
對于分布式應用提供的每個服務,使用接受器-連接器模式來使連接建立及服務初始化與由服務的兩個端點在連接和初始化之后執行的后續處理去耦合。
引入兩個工廠,生成已連接和初始化的服務處理器,用于實現應用的服務。第一個工廠,稱為接受器,創建并初始化傳輸端點,被動地在特定地址上偵聽來自遠地連接器的連接請求。第二個工廠,連接器,主動地發起到遠地接受器的連接。接受器和連接器都初始化相應的服務處理器,處理在連接上交換的數據。一旦服務處理器被連接和初始化,它們就執行應用特有的處理,一般不再與接受器和連接器進行交互。
在圖9-2中通過Booch類圖[2]演示了接受器-連接器模式中的參與者的結構。
服務處理器(Service Handler
):Service Handler實現應用服務,通常扮演客戶角色、服務器角色,或同時扮演這兩種角色。它提供掛鉤方法,由Acceptor或Connector調用,以在連接建立時啟用應用服務。此外,Service Handler還提供數據模式傳輸端點,其中封裝了一個I/O句柄,比如socket。一旦連接和初始化后,該端點被Service Handler用于與和其相連的對端交換數據。
接受器(Acceptor
):Acceptor是一個工廠,實現用于被動地建立連接并初始化與其相關聯的Service Handler的策略。此外,Acceptor包含有被動模式的傳輸端點工廠,它創建新的數據模式端點,由Service Handler用于在相連的對端間傳輸數據。通過將傳輸端點工廠綁定到網絡地址,比如Acceptor在其上偵聽的TCP端口號,Acceptor的open方法對該工廠進行初始化。
一旦初始化后,被動模式的傳輸端點工廠偵聽來自對端的連接請求。當連接請求到達時,Acceptor創建Service Handler,并使用它的傳輸端點工廠來將新連接接受進Service Handler中。
連接器(Connector
):Connector是一個工廠,實現用于主動地建立連接并初始化與其相關聯的Service Handler的策略。它提供方法,由其發起到遠地Acceptor的連接。同樣地,它還提供另一個方法,完成對Service Handler的啟用;該處理器的連接是被同步或異步地發起的。Connector使用兩個分開的方法來透明地支持異步連接建立。
圖9-2 Acceptor-Connector模式的參與者的結構
當連接建立時,Acceptor和Connector都通過調用Service Handler的啟用掛鉤方法來將其啟用。一旦Service Handler被Acceptor或Connector工廠完全初始化,它通常就不再與這些組件進行交互了。
分派器(Dispatcher
):為Acceptor,Dispatcher將在一或多個傳輸端點上接收到的連接請求多路分離給適當的Acceptor。Dispatcher允許多個Acceptor向其登記,以偵聽同時在不同端口上從不同對端而來的連接。
為Connector,Dispatcher處理異步發起的連接的完成。在這種情況下,當異步連接被建立時,Dispatcher回調Connector。Dispatcher允許多個Service Handler通過一個Connector來異步地發起和完成它們的連接。注意對于同步連接建立,Dispatcher并不是必需的,因為發起連接的線程控制也完成服務服務處理器的啟用。
Dispatcher通常使用事件多路分離模式來實現,這些模式由反應堆(Reactor)[3]或前攝器(Proactor)[4]來提供,它們分別處理同步和異步的多路分離。同樣地,Dispatcher也可以使用主動對象(Active Object)模式[5]來實現為單獨的線程或進程。
下面的部分描述接受器-連接器模式中Acceptor和Connector組件所執行的協作。我們檢查三種規范的情況:Acceptor、異步的Connector和同步的Connector。
圖9-3演示Acceptor和Service Handler之間的協作。這些協作被劃分為三個階段:
-
端點初始化階段:為被動地初始化連接,應用調用Acceptor的open方法。該方法創建被動模式的傳輸端點,將其綁定到網絡地址,例如,本地主機的IP地址和TCP端口號,并隨后偵聽來自對端Connector的連接請求。其次,open方法將Acceptor對象登記到Dispatcher,以使分派器能夠在連接事件到達時回調Acceptor。最后,應用發起Dispatcher的事件循環,等待連接請求從對端Connector到來。
-
服務初始化階段:當連接請求到達時,Dispatcher回調Acceptor的accept方法。該方法裝配以下活動所必需的資源:(1)創建新的Service Handler,(2)使用它的被動模式傳輸端點工廠來將連接接受進該處理器的數據模式傳輸端點中,以及(3)通過調用Service Handler的open掛鉤將其啟用。Service Handler的open掛鉤可以執行服務特有的初始化,比如分配鎖、派生線程、打開日志文件,和/或將該Service Handler登記到Dispatcher。
-
服務處理階段:在連接被動地建立和Service Handler被初始化后,服務處理階段開始了。在此階段,應用級通信協議,比如HTTP或IIOP,被用于在本地Service Handler和與其相連的遠地Peer之間、經由前者的peer_stream_端點交換數據。當交換完成,可關閉連接和Service Handler,并釋放資源。
圖9-3 Acceptor參與者之間的協作
Connector組件可以使用兩種常用方案來初始化它的Service Handler:同步的和異步的。同步的服務初始化對于以下情形來說是有用的:
- 如果建立連接的延遲非常低,例如,經由回路設備與在同一主機上的服務器建立連接;或是
- 如果有多個線程控制可用,并且使用不同的線程來同步地連接每個Service Handler有足夠的效率;或是
- 如果服務必須以固定順序初始化,而客戶不到連接建立不能執行其他有用的工作。
同樣地,異步服務初始化在相反的情形中是有用的:
- 如果連接延遲很高,并且有許多對端需要連接,例如,在高延遲WAN之上建立大量連接;或是
- 如果僅有單個線程控制可用,例如,如果OS平臺不提供應用級線程;或是
- 如果服務被初始化的順序不重要,或如果客戶應用必須在建立連接的同時執行另外的工作,比如刷新GUI。
同步的Connector情況中的參與者之間的協作可被劃分為以下三個階段:
-
連接發起階段:為在Service Handler和它的遠地Peer之間發起連接,應用調用Connector的connect方法。該方法阻塞調用線程的線程控制、直到連接同步完成,以主動地建立連接。
-
服務初始化階段:在連接完成后,Connector的connect方法調用complete方法來啟用Service Handler。complete方法通過調用Service_Handler的open掛鉤方法來完成啟用;open方法執行服務特有的初始化。
-
服務處理階段:此階段與Service Handler被Acceptor創建后所執行的服務處理階段相類似。特別地,一旦Service Handler被啟用,它使用與和其相連接的遠地Service Handler交換的數據來執行應用特有的服務處理。
同步服務初始化的協作如圖9-4所示。在此方案中,Connector將連接發起和服務初始化階段結合進單一的阻塞操作中。在此情況中,只為每個線程控制中的每次connect調用建立一個連接。
圖9-4 用于同步連接的Connector參與者之間的協作
異步的Connector中的參與者之間的協作可被劃分為以下三個階段:
-
連接發起階段:為在Service Handler和其遠地Peer之間發起一個連接,應用調用Connector的connect方法。就如同同步方案,Connector主動地建立連接。但是,在連接異步完成的同時,它不會阻塞調用者的線程控制。相反,它將Service Handler的傳輸端點(我們在此例中將其稱為peer_stream_)登記到Dispatcher,并將控制返回給它的調用者。
-
服務初始化階段:在連接異步完成后,Dispatcher回調Connector的complete方法。該方法通過調用Service Handler的open掛鉤來將其啟用。這個open掛鉤執行服務特有的初始化。
-
服務處理階段:此階段與前面描述的其他服務處理階段相類似。一旦Service Handler被啟用,它使用與和其相連接的遠地Service Handler交換的數據來執行應用特有的服務處理。
圖9-5演示這三個階段的使用異步連接建立的協作。在異步方案中,注意連接發起階段被暫時與服務初始化階段分離開來。這樣的去耦合使得多個連接發起(經由connect)和完成(經由complete)能夠在各自的線程控制中并行地進行。
圖9-5 用于異步連接的Connector參與者之間的協作
這一部分解釋使用接受器-連接器模式來構建通信軟件應用所涉及的步驟。這里的實現基于ACE OO網絡編程工具包[6]中的可復用組件和應用。ACE提供一組豐富的可復用C++包裝和構架組件,它們可在一系列OS平臺上執行常用的通信軟件任務。
接受器-連接器模式中的參與者被劃分為反應、連接和應用層,如圖9-6所示。
反應和連接層分別為分派事件和初始化服務執行通用的、與應用無關的策略。應用層通過提供建立連接和執行服務處理的具體類來實例化這些通用策略。這樣的事務分離增強了接受器-連接器模式實現中的可復用性、可移植性和可擴展性。
圖9-6 Acceptor-Connector模式實現中的參與者的分層和劃分
下面對接受器-連接器模式實現的討論從底部的反應層開始,并向上通過連接層和應用層。
反應層處理發生在由I/O句柄表示的傳輸端點(比如socket端點)上的事件。該層的兩個參與者,Initiation Dispatcher(發起分派器)和Event Handler(事件處理器),是由反應堆(Reactor)模式[3]定義的。該模式使得程序在單線程控制中就能夠高效地完成來自多個來源的多種類型的事件的多路分離。
反應層中的兩個主要角色是:
事件處理器:它規定由掛鉤方法[7]組成的接口,抽象地表示應用可提供的事件處理操作。例如,這些掛鉤方法表示這樣一些事件:新連接請求、異步開始的連接請求的完成,或是來自相連對端的數據的到達,等等。Acceptor和Connector組件是從Event Handler派生的具體的事件處理器。
發起分派器:為登記、移除和分派Event Handler定義接口。Synchronous Event Demultiplexer(同步的事件多路分離器),比如select[8]或WaitForMultipleObjects[9],通知Initiation Dispatcher何時回調應用特有的事件處理器,以響應特定類型的事件。常用事件包括連接接受事件、數據輸入和輸出事件,以及超時事件。
注意Initiation Dispatcher是9.6描述的Dispatcher的實現。一般而言,接受器-連接器Dispatcher可以是反應式、前攝式(Proactive)或多線程的。在這一實現中的特定的Initiation Dispatcher使用反應式模型來在單線程控制中多路分離和分派具體的事件處理器。在我們的例子中,Initiation Dispatcher是單體(Singleton)[10],因為我們只需要它的一個實例用于整個進程。
連接層:
- 創建Service Handler;
- 被動地或主動地將Service Handler連接到它們的遠地對端;以及
- 一旦連接,啟用Service Handler。
在此層中的所有行為都是完全通用的。特別地,注意下面描述的實現中的類是怎樣委托具體的IPC機制和Concrete Service Handler的;后者是由在9.8.3中描述的應用層實例化的。
應用層委托連接層的方式與連接層委托反應層的方式相類似。例如,反應層中的Initiation Dispatcher代表連接層處理與初始化有關的事件,比如異步的建立連接。
在連接層中有三個主要角色:Service Handler(服務處理器)、Acceptor和Connector。
-
服務處理器:該抽象類繼承自Event_Handler,并為客戶、服務器或同時扮演兩種角色的組件所提供的服務處理提供通用接口。應用必須通過繼承來定制此類,以執行特定類型的服務。Service Handler接口如下所示:
// PEER_STREAM is the type of the
// Concrete IPC mechanism.
template <class PEER_STREAM>
class Service_Handler : public Event_Handler
{
public:
// Pure virtual method (defined by a subclass).
virtual int open (void) = 0;
// Accessor method used by Acceptor and
// Connector to obtain the underlying stream.
PEER_STREAM &peer (void)
{
return peer_stream_;
}
// Return the address that we’re connected to.
PEER_STREAM::PEER_ADDR &remote_addr (void)
{
return peer_stream_.remote_addr ();
}
protected:
// Concrete IPC mechanism instance.
PEER_STREAM peer_stream_;
};
一旦Acceptor或Connector建立了連接,它們調用Service Handler的open掛鉤。該純虛方法必須被Concrete Service Handler子類定義;后者執行服務特有的初始化和后續處理。
連接器:該抽象類實現主動連接建立和初始化Service Handler的通用策略。它的接口如下所示:
// The SERVICE_HANDLER is the type of service.
// The PEER_CONNECTOR is the type of concrete
// IPC active connection mechanism.
template <class SERVICE_HANDLER,
class PEER_CONNECTOR>
class Connector : public Event_Handler
{
public:
enum Connect_Mode
{
SYNC, // Initiate connection synchronously.
ASYNC // Initiate connection asynchronously.
};
// Initialization method.
Connector (void);
// Actively connecting and activate a service.
int connect (SERVICE_HANDLER *sh,
const PEER_CONNECTOR::PEER_ADDR &addr,
Connect_Mode mode);
protected:
// Defines the active connection strategy.
virtual int connect_service_handler(SERVICE_HANDLER *sh,
const PEER_CONNECTOR::PEER_ADDR &addr,
Connect_Mode mode);
// Register the SERVICE_HANDLER so that it can
// be activated when the connection completes.
int register_handler (SERVICE_HANDLER *sh, Connect_Mode mode);
// Defines the handler’s concurrency strategy.
virtual int activate_service_handler(SERVICE_HANDLER *sh);
// Activate a SERVICE_HANDLER whose
// non-blocking connection completed.
virtual int complete (HANDLE handle);
private:
// IPC mechanism that establishes
// connections actively.
PEER_CONNECTOR connector_;
// Collection that maps HANDLEs
// to SERVICE_HANDLER *s.
Map_Manager<HANDLE, SERVICE_HANDLER *>handler_map_;
// Inherited from the Event_Handler -- will be
// called back by Eactor when events complete
// asynchronously.
virtual int handle_event (HANDLE, EVENT_TYPE);
};
// Useful "short-hand" macros used below.
#define SH SERVICE_HANDLER
#define PC PEER_CONNECTOR
Conncetor通過特定類型的PEER CONNECTOR和SERVICE HANDLER被參數化。PEER CONNECTOR提供的傳輸機制被Connector用于主動地建立連接,或是同步地、或是異步地。SERVICE HANDLER提供的服務對與相連的對端交換的數據進行處理。C++參數化類型被用于使(1)連接建立策略與(2)服務處理器類型、網絡編程接口和傳輸層連接協議去耦合。
參數化類型是有助于提高可移植性的實現決策。例如,它們允許整體地替換Connector所用的IPC機制。這使得Connector的連接建立代碼可在含有不同網絡編程接口(例如,有socket,但沒有TLI;反之亦然)的平臺間進行移植。例如,取決于平臺是支持socket還是TLI[11],PEER CONNECTOR模板參數可以通過SOCK Connector或TLI Connector來實例化。使用參數化類型的另一個動機是改善運行時效率,因為模板實例化發生在編譯時。
更為動態的去耦合可以經由繼承和多態、通過使用[10]中描述的工廠方法(Factory Method)和策略(Strategy)模式來完成。例如,Connector可以存儲指向PEER CONNECTOR基類的指針。根據從工廠返回的PEER CONNECTOR的子類,這個PEER CONNECTOR的connect方法可在運行時被動態地綁定。一般而言,在參數化類型和動態綁定之間的權衡是參數化類型可能帶來額外的編譯/鏈接時開銷,而動態綁定可能帶來額外的運行時開銷。
connect方法是應用用以通過Connector來發起連接的入口。它的實現如下所示:
template <class SH, class PC> int
Connector<SH, PC>::connect(SERVICE_HANDLER *service_handler,
const PEER_CONNECTOR::PEER_ADDR &addr,
Connect_Mode mode)
{
connect_service_handler (service_handler, addr, mode);
}
該方法使用橋接(Bridge)模式[10]來使Concrete Connector能透明地修改連接策略,而不用改變組件接口。為此,connect方法委托Connector的連接策略,connect_service_handler,來發起連接。如下所示:
template <class SH, class PC> int
Connector<SH, PC>::connect_service_handler
(SERVICE_HANDLER *service_handler,
const PEER_CONNECTOR::PEER_ADDR &remote_addr,
Connect_Mode mode)
{
// Delegate to concrete PEER_CONNECTOR
// to establish the connection.
if (connector_.connect (*service_handler,
remote_addr,
mode) == -1)
{
if (mode == ASYNC && errno == EWOULDBLOCK)
{
// If connection doesn’t complete immediately
// and we are using non-blocking semantics
// then register this object with the
// Initiation_Dispatcher Singleton so it will
// callback when the connection is complete.
Initiation_Dispatcher::instance
()->register_handler (this, WRITE_MASK);
// Store the SERVICE_HANDLER in the map of
// pending connections.
handler_map_.bind
(connector_.get_handle (), service_handler);
}
}
else if (mode == SYNC)
// Activate if we connect synchronously.
activate_service_handler (service_handler);
}
如圖9-7所示,如果Connect_Mode參數的值是SYNC,一旦連接同步地完成,SERVICE HANDLER將會被啟用。該圖與圖9-4相類似,但是提供了另外的實現細節,比如get_handle和handle_event掛鉤方法的使用。
圖9-7 用于同步連接的Connector參與者之間的協作
為高效地與多個Peer相連,Connector可能還需要主動、異步地建立連接,也就是,不阻塞調用者。如圖9-8所示,異步行為通過將ASYNC連接模式傳遞給Connector::connect來指定。該圖與圖9-5相類似,但是還提供了其他與當前實現相應的細節。
一旦實例化,PEER CONNECTOR類提供具體的IPC機制來同步或異步地發起連接。這里所顯示的Connector模式的實現使用OS和通信協議棧所提供的異步連接機制。例如,在UNIX或Win32上,Connector可以將socket設置進非阻塞模式,并使用像select或WaitForMultipleObject這樣的事件多路分離器來確定連接何時完成。
為處理還未完成的異步連接,Connector維護Service Handler映射表。因為Connector繼承自Event Handler,當連接完成時,Initiation Dispatcher可以自動回調Connector的handle_event方法。
handle_event方法是一個適配器(Adapter)[10],它將Initiation Dispatcher的事件處理接口轉換為對Connector模式的complete方法的調用。
圖9-8 用于異步連接的Connector參與者之間的協作
Connector的handle_event方法如下所示:
template <class SH, class PC> int
Connector<SH, PC>::handle_event (HANDLE handle, EVENT_TYPE type)
{
// Adapt the Initiation_Dispatcher’s event
// handling API to the Connector’s API.
complete (handle);
}
complete方法啟用剛剛成功完成非阻塞連接的SERVICE HANDLER,如下所示:
template <class SH, class PC> int
Connector<SH, PC>::complete (HANDLE handle)
{
SERVICE_HANDLER *service_handler = 0;
// Locate the SERVICE_HANDLER corresponding
// to the HANDLE.
handler_map_.find (handle, service_handler);
// Transfer I/O handle to SERVICE_HANDLER *.
service_handler->set_handle (handle);
// Remove handle from Initiation_Dispatcher.
Initiation_Dispatcher::instance
()->remove_handler (handle, WRITE_MASK);
// Remove handle from the map.
handler_map_.unbind (handle);
// Connection is complete, so activate handler.
activate_service_handler (service_handler);
}
complete方法在其內部映射表中查找并移除已連接的SERVICE HANDLER,并將I/O句柄傳遞給SERVICE HANDLER。最后,它通過調用activate_service_handler方法初始化SERVICE HANDLER。該方法委托由SERVICE HANDLER的open掛鉤指定的并發策略。如下所示:
template <class SH, class PC> int
Connector<SH, PC>::activate_service_handler
(SERVICE_HANDLER *service_handler)
{
service_handler->open ();
}
Service Handler的open掛鉤在連接成功建立時被調用。注意該掛鉤都將被調用,不管(1)連接是同步還是異步發起的,或(2)它們是被主動還是被動連接的。這樣的統一性使得開發者有可能編寫這樣的Service Handler,其處理可以完全地與它們是怎樣被連接和初始化的去耦合。
接受器(Acceptor
):該抽象類為被動連接建立和初始化Service Handler實現通用的策略。Acceptor的接口如下所示:
// The SERVICE_HANDLER is the type of service.
// The PEER_ACCEPTOR is the type of concrete
// IPC passive connection mechanism.
template <class SERVICE_HANDLER,
class PEER_ACCEPTOR>
class Acceptor : public Event_Handler
{
public:
// Initialize local_addr transport endpoint factory
// and register with Initiation_Dispatcher Singleton.
virtual int open(const PEER_ACCEPTOR::PEER_ADDR &local_addr);
// Factory Method that creates, connects, and
// activates SERVICE_HANDLER’s.
virtual int accept (void);
protected:
// Defines the handler’s creation strategy.
virtual SERVICE_HANDLER *make_service_handler (void);
// Defines the handler’s connection strategy.
virtual int accept_service_handler(SERVICE_HANDLER *);
// Defines the handler’s concurrency strategy.
virtual int activate_service_handler(SERVICE_HANDLER *);
// Demultiplexing hooks inherited from Event_Handler,
// which is used by Initiation_Dispatcher for
// callbacks.
virtual HANDLE get_handle (void) const;
virtual int handle_close (void);
// Invoked when connection requests arrive.
virtual int handle_event (HANDLE, EVENT_TYPE);
private:
// IPC mechanism that establishes
// connections passively.
PEER_ACCEPTOR peer_acceptor_;
};
// Useful "short-hand" macros used below.
#define SH SERVICE_HANDLER
#define PA PEER_ACCEPTOR
Acceptor通過特定類型的PEER ACCEPTOR和SERVICE HANDLER被參數化。PEER ACCEPTOR提供的傳輸機制被Acceptor用于被動地建立連接。SERVICE HANDLER提供的服務對與遠地對端交換的數據進行處理。注意SERVICE HANDLER是由應用層提供的具體的服務處理器。
參數化類型使Acceptor的連接建立策略與服務處理器的類型、網絡編程接口及傳輸層連接發起協議去耦合。就如同Connector一樣,通過允許整體地替換Acceptor所用的機制,參數化類型的使用有助于提高可移植性。這使得連接建立代碼可在含有不同網絡編程接口(比如有socket,但沒有TLI;反之亦然)的平臺間移植。例如,取決于平臺能夠更為高效地支持socket還是TLI,PEER ACCEPTOR模板參數可以通過SOCK Acceptor或TLI Acceptor來實例化。
下面給出Acceptor的方法的實現。應用通過調用Acceptor的open方法來將其初始化。如下所示:
template <class SH, class PA> int
Acceptor<SH, PA>::open
(const PEER_ACCEPTOR::PEER_ADDR &local_addr)
{
// Forward initialization to the PEER_ACCEPTOR.
peer_acceptor_.open (local_addr);
// Register with Initiation_Dispatcher, which
// ‘‘double-dispatches’’ without get_handle()
// method to extract the HANDLE.
Initiation_Dispatcher::instance
()->register_handler (this, READ_MASK);
}
local_addr被傳遞給open方法。該參數含有網絡地址,例如,本地主機的IP地址和TCP端口號,用于偵聽連接。Open方法將此地址轉發給PEER ACCEPTOR定義的被動連接接受機制。該機制初始化傳輸端點工廠,由后者將地址廣告給有興趣與此Acceptor連接的客戶。
傳輸端點工廠的行為由用戶所實例化的PEER ACCEPTOR的類型來決定。例如,它可以是socket[13]、TLI[14]、STREAM管道[15]、Win32命名管道等的C++包裝。
在傳輸端點工廠被初始化后,open方法將其自身登記到Initiation Dispatcher。Initiation Dispatcher執行“雙重分派”,回調Acceptor的get_handle方法,以獲取底層傳輸端點工廠的HANDLE。如下所示:
template <class SH, class PA> HANDLE
Acceptor<SH, PA>::get_handle (void)
{
return peer_acceptor_.get_handle ();
}
Initiation Dispatcher在內部表中存儲此HANDLE。Synchronous Event Demultipler(同步事件多路分離器),比如select,隨即被用于檢測和多路分離到來的來自客戶的連接請求。因為Acceptor類繼承自Event Handler,當連接從對端到達時,Initiation Dispatcher可以自動回調Acceptor的handle_event方法。該方法是一個適配器(Adapter),它將Initiation Dispatcher的事件處理接口轉換為對Acceptor的accept方法的調用。如下所示:
template <class SH, class PA> int
Acceptor<SH, PA>::handle_event (HANDLE, EVENT_TYPE)
{
// Adapt the Initiation_Dispatcher’s event handling
// API to the Acceptor’s API.
accept ();
}
如下所示,accept方法是一個模板方法(Template Method)[10],它為創建新SERVICE HANDLER、將連接接受進其中并啟用服務而實現接受器-連接器模式的被動初始化策略:
template <class SH, class PA> int
Acceptor<SH, PA>::accept (void)
{
// Create a new SERVICE_HANDLER.
SH *service_handler = make_service_handler ();
// Accept connection from client.
accept_service_handler (service_handler);
// Activate SERVICE_HANDLER by calling
// its open() hook.
activate_service_handler (service_handler);
}
該方法非常簡潔,因為它將所有低級細節都分解進具體的SERVICE HANDLER和PEER ACCEPTOR中,后二者通過參數化類型被實例化,并可被Acceptor的子類定制。特別地,因為accept是模板方法,子類可以擴展Acceptor的任意或所有的連接建立和初始化策略。這樣的靈活性使得開發者有可能編寫這樣的Service Handler,其行為與它們被被動地連接和初始化的方式是相分離的。
make_service_handler工廠方法定義Acceptor用于創建SERVICE HANDLER的缺省策略。如下所示:
template <class SH, class PA> SH *
Acceptor<SH, PA>::make_service_handler (void)
{
return new SH;
}
缺省行為使用了“請求策略”(demand strategy),它為每個新連接創建新的SERVICE HANDLER。但是,Acceptor的子類可以重定義這一策略,以使用其他策略創建SERVICE HANDLE,比如創建單獨的單體(Singleton)[10]或從共享庫中動態鏈接SERVICE HANDLER。
accept_service_handler方法在下面定義Acceptor所用的SERVICE HANDLER連接接受策略:
template <class SH, class PA> int
Acceptor<SH, PA>::accept_service_handler(SH *handler)
{
peer_acceptor_->accept (handler->peer ());
}
缺省行為委托PEER ACCEPTOR所提供的accept方法。子類可以重定義accept_service_handler方法,以執行更為復雜的行為,比如驗證客戶的身份,以決定是接受還是拒絕連接。
Activate_service_handler定義Acceptor的SERVICE HANDLER并發策略:
template <class SH, class PA> int
Acceptor<SH, PA>::activate_service_handler(SH *handler)
{
handler->open ();
}
該方法的缺省行為是通過調用SERVICE HANDLER的open掛鉤將其啟用。這允許SERVICE HANDLER選擇它自己的并發策略。例如,如果SERVICE HANDLER繼承自Event Handler,它可以登記到Initiation Dispatcher,從而在事件發生在SERVICE HANDLER的PEER STREAM通信端點上時,使Initiation Dispatcher能夠分派其handle_event方法。Concrete Acceptor可以重定義此策略,以完成更為復雜的并發啟用。例如,子類可以使SERVICE HANDLER成為主動對象(Active Object)[5],使用多線程或多進程來處理數據。
當Acceptor終止時,無論是由于錯誤還是由于整個應用的關閉,Initiation Dispatcher都調用Acceptor的handle_close方法,后者可以釋放任何動態獲取的資源。在此例中,handle_close方法簡單地將close請求轉發給PEER ACCEPTOR的傳輸端點工廠。如下所示:
template <class SH, class PA> int
Acceptor<SH, PA>::handle_close (void)
{
peer_acceptor_.close ();
}
應用層提供具體的進程間通信(IPC)機制和具體的Service Handler。IPC機制被封裝在C++類中,以簡化編程、增強復用,并使開發者能夠整個地替換IPC機制。例如,9.9中使用的SOCK Acceptor、SOCK Connector,以及SOCK Stream類是ACE C++ socket包裝類庫[11]的一部分。它們通過高效、可移植和類型安全的C++包裝來封裝像TCP和SPX這樣的面向連接協議的面向流的語義。
應用層中的三個主要角色描述如下:
具體的服務處理器(Concrete Service Handler
):該類定義具體的應用服務,由Concrete Acceptor或Concrete Connector啟用。Concrete Service Handler通過特定類型的C++ IPC包裝(它與和其相連的對端進行數據交換)來實例化。
具體的連接器(Concrete Connector
):該類通過具體的參數化類型參數SERVICE HANDLER和PEER CONNECTOR來實例化通用的Connector工廠。
具體的接受器(Concrete Acceptor
):該類通過具體的參數化類型參數SERVICE HANDLER和PEER ACCEPTOR來實例化通用的Acceptor工廠。
Concrete Service Handler還可以定義服務的并發策略。例如,Service Handler可以從Event Handler繼承,并采用反應堆(Reactor)[3]模式來在單線程控制中處理來自對端的數據。相反,Service Handler也可以使用主動對象(Active Object)模式[5]處理到來的數據,而其所在線程控制與Acceptor連接它所用的不相同。下面,我們為我們的Gateway例子實現Concrete Service Handler,演示怎樣靈活地配置若干不同的并發策略,而又不影響接受器-連接器模式的結構或行為。
在9.9的示例代碼中,SOCK Connector和SOCK Acceptor是分別用于主動和被動地建立連接的IPC機制。同樣地,SOCK Stream被用作數據傳輸遞送機制。但是,通過其他機制(比如TLI Connector或Named Pipe Acceptor)來參數化Connector和Acceptor也是相當直接的,因為IPC機制被封裝在C++包裝類中。同樣地,通過使用不同的PEER STREAM,(比如SVR4 UNIX TLI Stream或Win32 Named Pipe Stream)來參數化Concrete Service Handler,很容易改變數據傳輸機制。
9.9演示怎樣實例化Concrete Service Handler、Concrete Connector和Concrete Acceptor,實現9.2中描述的Peer和Gateway。這個特定的應用層例子定制連接層中的Connector和Acceptor組件所提供的通用初始化策略。
下面的代碼演示9.2中描述的Peer和Gateway怎樣使用接受器-連接器模式來簡化連接建立和服務初始化。9.9.1演示Peer怎樣扮演被動角色,9.9.2演示Gateway怎樣在與被動的Peer的連接建立中扮演主動角色。
圖9-9演示Concrete Acceptor和Concrete Service Handler組件是怎樣在Peer中構造的。該圖中的Acceptor組件與圖9-11中的Connector組件是互補的。
用于與Gateway
通信的服務處理器:如下所示的Status Handler、Bulk Data Handler和Command Handler類處理發送到Gateway和從Gateway接收的路由消息。因為這些Concrete Service Handler類繼承自Service Handler,它們可以被Acceptor被動地初始化。
為演示接受器-連接器模式的靈活性,這些Service Handler中的每個open例程都可以實現不同的并發策略。特別地,當Status Handler被啟用時,它運行在單獨的線程中;Bulk Data Handler作為單獨的進程運行;而Command Handler運行在與Initiation Dispatcher相同的線程中,后者為Acceptor工廠進行連接請求的多路分離。注意這些并發策略的改變并不影響Acceptor的實現,它是通用的,因而也是高度靈活和可復用的。
我們從定義一個Service Handler開始,它為基于socket的數據傳輸使用SOCK Stream:
typedef Service_Handler <SOCK_Stream>PEER_HANDLER;
圖9-9 對端的Acceptor參與者的結構
PEER HANDLER的typedef構成所有后續服務處理器的基礎。例如,Status Handler類處理發送到Gateway和從Gateway接收的狀態數據:
class Status_Handler : public PEER_HANDLER
{
public:
// Performs handler activation.
virtual int open (void)
{
// Make handler run in separate thread (note
// that Thread::spawn requires a pointer to
// a static method as the thread entry point).
Thread::spawn (&Status_Handler::service_run, this);
}
// Static entry point into thread, which blocks
// on the handle_event () call in its own thread.
static void *service_run (Status_Handler *this_)
{
// This method can block since it
// runs in its own thread.
while (this_->handle_event () != -1)
continue;
}
// Receive and process status data from Gateway.
virtual int handle_event (void)
{
char buf[MAX_STATUS_DATA];
stream_.recv (buf, sizeof buf);
// ...
}
// ...
};
PEER HANDLER還可被子類化,以生成具體的服務處理器,處理大塊數據和命令。例如,Bulk Data Handler類處理發送到Gateway和從Gateway接收的大塊數據:
class Bulk_Data_Handler : public PEER_HANDLER
{
public:
// Performs handler activation.
virtual int open (void)
{
// Handler runs in separate process.
if (fork () == 0) // In child process.
// This method can block since it
// runs in its own process.
while (handle_event () != -1)
continue;
// ...
}
// Receive and process bulk data from Gateway.
virtual int handle_event (void)
{
char buf[MAX_BULK_DATA];
stream_.recv (buf, sizeof buf);
// ...
}
// ...
};
Command Handler類處理發送到Gateway和從Gateway接收的命令。
class Command_Handler : public PEER_HANDLER
{
public:
// Performs handler activation.
virtual int open (void)
{
// Handler runs in same thread as main
// Initiation_Dispatcher singleton.
Initiation_Dispatcher::instance
()->register_handler (this, READ_MASK);
}
// Receive and process command data from Gateway.
virtual int handle_event (void)
{
char buf[MAX_COMMAND_DATA];
// This method cannot block since it borrows
// the thread of control from the
// Initiation_Dispatcher.
stream_.recv (buf, sizeof buf);
// ...
}
//...
};
用于創建Peer Service Handler
的接受器:如下所示的s_acceptor、bd_acceptor和c_acceptor對象是Concrete Acceptor工廠實例,它們分別創建并啟用Status Handler、Bulk Data Handler和Command Handler。
// Accept connection requests from Gateway and
// activate Status_Handler.
Acceptor<Status_Handler, SOCK_Acceptor> s_acceptor;
// Accept connection requests from Gateway and
// activate Bulk_Data_Handler.
Acceptor<Bulk_Data_Handler, SOCK_Acceptor> bd_acceptor;
// Accept connection requests from Gateway and
// activate Command_Handler.
Acceptor<Command_Handler, SOCK_Acceptor> c_acceptor;
注意模板和動態綁定的使用是怎樣允許特定細節靈活地變化的。特別地,在整個這一部分中,當并發策略被修改時,沒有Acceptor組件發生變化。這樣的靈活性的原因是并發策略已被分解進Service Handler中,而不是與Acceptor耦合在一起。
圖9-10 對端中的Acceptor組件的對象圖
Peer
主函數:主程序通過調用具體的Acceptor工廠的open掛鉤(以每個服務的TCP端口為參數)來對它們進行初始化。如9.8.2所示,每個Acceptor工廠自動地在它的open方法中將其自身登記到Initiation Dispatcher的實例。
// Main program for the Peer.
int main (void)
{
// Initialize acceptors with their
// well-known ports.
s_acceptor.open (INET_Addr (STATUS_PORT));
bd_acceptor.open (INET_Addr (BULK_DATA_PORT));
c_acceptor.open (INET_Addr (COMMAND_PORT));
// Event loop that handles connection request
// events and processes data from the Gateway.
for (;;)
Initiation_Dispatcher::instance()->handle_events ();
}
一旦Acceptor被初始化,主程序進入事件循環,使用Initiation Dispatcher來檢測來自Gateway的連接請求。當連接到達時,Initiation Dispatcher回調適當的Acceptor,由其創建適當的PEER HANDLER來執行服務、將連接接受進處理器、并啟用處理器。
圖9-10演示在與Gateway(如圖9-12所示)的四個連接被建立、以及四個Service Handler被創建和啟用后,Peer中的Concrete Acceptor組件之間的關系。在Concrete Service Handler與Gateway交換數據的同時,三個Acceptor也在主線程中持續地偵聽新連接。
圖9-11演示Concrete Connector和Concrete Service Handler組件是怎樣在假想的Gateway配置中構造的。該圖中的Connector組件與圖9-9中的Acceptor組件是互補的。
圖9-11 網關的Connector參與者的結構
用于Gateway
路由的服務處理器:如下所示的Status Router、Buld Data Router和Command Router類將它們從源Peer接收到的數據路由到一或多個目的Peer。因為這些Concrete Service Handler類繼承自Service Handler,它們可以被Connector主動地連接和初始化。
為演示接受器-連接器模式的靈活性,Service Handler中的每個open例程實現不同的并發策略。特別地,當Status Router被啟用時,它運行在單獨的線程中;Bulk Data Router作為單獨的進程運行;而Command Router運行在與Initiation Dispatcher相同的線程中,后者為Connector工廠進行連接完成事件的多路分離。就如同Acceptor一樣,注意這些并發策略的變動并不會影響Connector的實現,它是通用的,因而也是高度靈活和可復用的。
我們從定義一個Service Handler開始,它為基于socket的數據傳輸而定制:
typedef Service_Handler <SOCK_Stream>PEER_ROUTER;
該類構成所有后續路由服務的基礎。例如,Status Router類路由狀態數據到Peer,或路由來自Peer的數據:
class Status_Router : public PEER_ROUTER
{
public:
// Activate router in separate thread.
virtual int open (void)
{
// Thread::spawn requires a pointer to a
// static method as the thread entry point).
Thread::spawn (&Status_Router::service_run, this);
}
// Static entry point into thread, which blocks
// on the handle_event() call in its own thread.
static void *service_run (Status_Router *this_)
{
// This method can block since it
// runs in its own thread.
while (this_->handle_event () != -1)
continue;
}
// Receive and route status data from/to Peers.
virtual int handle_event (void)
{
char buf[MAX_STATUS_DATA];
peer_stream_.recv (buf, sizeof buf);
// Routing takes place here...
}
// ...
};
PEER ROUTER可被子類化,以生成用于路由大塊數據和命令具體的服務處理器。例如,Bulk Data Router路由數據到Peer,或路由來自Peer的數據:
class Bulk_Data_Router : public PEER_ROUTER
{
public:
// Activates router in separate process.
virtual int open (void)
{
if (fork () == 0) // In child process.
// This method can block since it
// runs in its own process.
while (handle_event () != -1)
continue;
// ...
}
// Receive and route bulk data from/to Peers.
virtual int handle_event (void)
{
char buf[MAX_BULK_DATA];
peer_stream_.recv (buf, sizeof buf);
// Routing takes place here...
}
};
Command Router類路由命令數據到Peer,或路由來自Peer的命令數據:
class Command_Router : public PEER_ROUTER
{
public:
// Activates router in same thread as Connector.
virtual int open (void)
{
Initiation_Dispatcher::instance
()->register_handler (this, READ_MASK);
}
// Receive and route command data from/to Peers.
virtual int handle_event (void)
{
char buf[MAX_COMMAND_DATA];
// This method cannot block since it borrows the
// thread of control from the Initiation_Dispatcher.
peer_stream_.recv (buf, sizeof buf);
// Routing takes place here...
}
};
用于創建Peer Service Handler
的接受器:下面的typedef定義為PEER ROUTER而定制的connector工廠:
typedef Connector<PEER_ROUTERS, SOCK_Connector>PEER_CONNECTOR;
不像 Concrete Acceptor組件,我們只需要單個Concrete Connector。原因是每個Concrete Acceptor都被用作創建特定類型的Concrete Service Handler(比如Bulk Data Handler或Command Handler)的工廠。因此,必須預先知道全部的類型,從而使多種Concrete Acceptor類型成為必要。相反,傳遞給Connector的connect方法的Concrete Service Handler是在外部初始化的。因此,它們可以統一地被當作PEER ROUTER處理。
Gateway
主函數:Gateway的主程序如下所示。get_peer_addrs函數創建Status、Bulk Data和Command Router,通過Gateway路由消息。該函數(它的實現沒有給出)從配置文件或名字服務中讀取Peer地址列表。每個Peer地址含有IP地址和端口號。一旦Router被初始化,上面定義的Connector工廠通過將ASYNC標志傳遞給connect方法來異步地發起所有連接。
// Main program for the Gateway.
// Obtain an STL vector of Status_Routers,
// Bulk_Data_Routers, and Command_Routers
// from a config file.
void get_peer_addrs (vector<PEER_ROUTERS> &peers);
int main (void)
{
// Connection factory for PEER_ROUTERS.
PEER_CONNECTOR peer_connector;
// A vector of PEER_ROUTERs that perform
// the Gateway’s routing services.
vector<PEER_ROUTER> peers;
// Get vector of Peers to connect with.
get_peer_addrs (peers);
// Iterate through all the Routers and
// initiate connections asynchronously.
for (vector<PEER_ROUTER>::iterator i = peers.begin ();
i != peers.end ();
i++)
{
PEER_ROUTER &peer = *i;
peer_connector.connect (peer,
peer.remote_addr (),
PEER_CONNECTOR::ASYNC);
}
// Loop forever handling connection completion
// events and routing data from Peers.
for (;;)
Initiation_Dispatcher::instance()->handle_events ();
/* NOTREACHED */
}
所有連接都被異步地調用。它們通過Connector的complete方法并發地完成,該方法在Initiation Dispatcher的事件循環中被回調。此事件循環還為Command Router對象多路分離和分派路由事件;該對象運行在Initiation Dispatcher的線程控制中。Status Router和Bulk Data Router分別執行在單獨的線程和進程中。
圖9-12演示在與Peer(如圖9-10所示)的四個連接被建立、以及四個Concrete Service Handler被創建和啟用后,Gateway中的組件之間的關系。該圖演示到另一個Peer的四個連接,它們被Connector“擁有”,還沒有完成。當所有Peer連接完全建立時,Gateway將路由并轉發由Peer發送給它的消息。
圖9-12 網關中的Connector組件的對象圖
接受器-連接器模式已被用于廣泛的構架、工具包和系統中:
UNIX網絡超級服務器:比如inetd[13]、listen[14]以及來自ASX構架[6]的Service Configurator(服務配置器)看守。這些超級服務器利用主Acceptor進程,在一組通信端口上偵聽連接。每個端口都和與通信有關的服務(比如標準的Internet服務ftp、telnet、daytime和echo)相關聯。接受器進程使inetd超級服務器的功能分解為兩個分離的部分:一個用于建立連接,另一個用于接收和處理來自對端的的請求。當服務請求在被監控的端口上到達時,接受器進程接受請求,并分派適當的預登記的處理器來執行服務。
CORBA ORB:許多CORBA實現中的ORB核心層使用接受器-連接器來在客戶請求ORB服務時被動地初始化服務器對象實現。[17]描述接受器-連接器模式怎樣被用于實現The ACE ORB(TAO)[18];TAO是CORBA一種實時實現。
WWW瀏覽器:像Netscape和Internet Explorer這樣的WWW瀏覽器中的HTML解析組件使用connector組件的異步版本來建立與服務器的連接;這些服務器與HTML頁面中嵌入的圖像相關聯。這樣的行為是特別重要的,于是多個HTTP連接就可被異步地發起,以避免阻塞瀏覽器主事件循環。
Ericsson EOS呼叫中心管理系統:該系統使用接受器-連接器模式來使應用級呼叫中心管理器事件服務器[19]主動地與分布式中央管理系統中的被動的超級用戶建立連接。
Spectrum項目:Spectrum項目的高速醫學圖像傳輸子系統[20]使用接受器-連接器模式來為存儲大型醫學圖像被動地建立連接,并初始化應用服務。一旦連接被建立,應用就發送數兆字節的醫學圖像給圖像倉庫;或從圖像倉庫中進行接收。
ACE 構架:在本論文中描述的Service Handler、Connector和Acceptor類的實現在ACE面向對象網絡編程構架[6]中作為可復用組件提供。
接受器-連接器模式提供以下好處:
增強面向對象軟件的可復用性、可移植性和可擴展性:通過使服務初始化機制與后續服務處理去耦合來實現。例如,Acceptor和Connector中的應用無關的機制是可復用的組件,它們知道怎樣(1)分別主動和被動地建立連接,以及(2)一旦連接被建立,初始化相關聯的Service Handler。相反,Service Handler知道怎樣執行應用特有的服務處理。
這樣的事務分離是通過使初始化策略與服務處理策略去耦合來完成的。因而,每種策略都可以獨立地發展。用于主動初始化的策略可以只編寫一次,放進類庫或構架中,并通過繼承、對象合成或模板實例化來復用。因而,不需要為每個應用都重寫同樣的主動初始化代碼。相反,服務可以根據不同的應用需求進行變化。通過使用Service Handler來參數化Acceptor和Connector,可以使這樣的變化的影響被局限在軟件的少量組件中。
改善應用健壯性:應用健壯性是通過徹底地使Service Handler和Acceptor去耦合來改善的。這樣的去耦合確保了被動模式傳輸端點工廠peer_acceptor_不會偶然地被用于讀寫數據。這消除了在使用像socket或TLI[11]這樣的弱類型網絡編程接口時,可能發生的一類常見錯誤。
接受器-連接器模式有以下缺點:
額外的間接性:與直接使用底層的網絡編程接口相比較,接受器-連接器模式可能帶來額外的間接性。但是,支持參數化類型的語言(比如C++、Ada或Eiffel)可以較小的代價代價實現這些模式,因為編譯器可以內聯用于實現這些模式的方法調用。
額外的復雜性:對于簡單客戶應用(使用單個網絡編程接口與單個服務器相連,并執行單項服務)來說,該模式可能會增加不必要的復雜性。
接受器-連接器模式使用模板方法(Template Method)和工廠方法(Factory Method)模式[10]。Acceptor的accept和Connector的connect及complete函數是模板方法,它們在連接建立時為連接到遠地對端并初始化Service Handler而實現通用的服務策略。模板方法的使用使子類可以對創建、連接和啟用Concrete Service Handler的特定細節進行修改。工廠方法被用于使Service Handler的創建與它的后續使用去耦合。
接受器-連接器模式有與客戶-分派器-服務器(Client-Dispatcher-Service)模式(在[21]中描述)類似的意圖。它們都關心使主動連接建立與后續服務去耦合。主要的區別是接受器-連接器模式同時致力于同步和異步連接的被動和主動服務初始化,而客戶-分派器-服務器)模式只專注于同步連接建立。
感謝Frank Buschmann和Hans Rohnert對本論文提出的有益意見。
[1] W. R. Stevens, TCP/IP Illustrated, Volume 1. Reading, Massachusetts: Addison Wesley, 1993.
[2] G. Booch, Object Oriented Analysis and Design with Applications (2nd Edition). Redwood City, California: Benjamin/Cummings, 1993.
[3] D. C. Schmidt, “Reactor: An Object Behavioral Pattern for Concurrent Event Demultiplexing and Event Handler Dispatching,” in Pattern Languages of Program Design (J. O. Coplien and D. C. Schmidt, eds.), pp. 529–545, Reading, MA: Addison-Wesley, 1995.
[4] T. Harrison, I. Pyarali, D. C. Schmidt, and T. Jordan, “Proactor – An Object Behavioral Pattern for Dispatching Asynchronous Event Handlers,” in The 4th Pattern Languages of Programming Conference (Washington University technical report #WUCS-97-34), September 1997.
[5] R. G. Lavender and D. C. Schmidt, “Active Object: an Object Behavioral Pattern for Concurrent Programming,” in Pattern Languages of Program Design (J. O. Coplien, J. Vlissides, and N. Kerth, eds.), Reading, MA: Addison-Wesley, 1996.
[6] D. C. Schmidt, “ACE: an Object-Oriented Framework for Developing Distributed Applications,” in Proceedings of the 6th USENIX C++ Technical Conference, (Cambridge, Massachusetts), USENIX Association, April 1994.
[7] W. Pree, Design Patterns for Object-Oriented Software Development. Reading, MA: Addison-Wesley, 1994.
[8] W.R.Stevens,UNIX Network Programming, Second Edition. Englewood Cliffs, NJ: Prentice Hall, 1997.
[9] H. Custer, Inside Windows NT. Redmond, Washington: Microsoft Press, 1993.
[10] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.
[11] D. C. Schmidt, T. H. Harrison, and E. Al-Shaer, “Object-Oriented Components for High-speed Network Programming,” in Proceedings of the 1st Conference on Object-Oriented Technologies and Systems,(Monterey,CA), USENIX, June 1, 995.
[12] D. C. Schmidt, “IPC SAP: An Object-Oriented Interface to Interprocess Communication Services,” C++ Report,vol.4, November/December 1992.
[13] W. R. Stevens, UNIX Network Programming, First Edition. Englewood Cliffs, NJ: Prentice Hall, 1990.
[14] S. Rago, UNIX System V Network Programming. Reading, MA: Addison-Wesley, 1993.
[15] D. L. Presotto and D. M. Ritchie, “Interprocess Communication in the Ninth Edition UNIX System,” UNIX Research System Papers, Tenth Edition, vol. 2, no. 8, pp. 523–530, 1990.
[16] Object Management Group, The Common Object Request Broker: Architecture and Specification, 2.0 ed., July 1995.
[17] D. C. Schmidt and C. Cleeland, “Applying Patterns to Develop Extensible ORB Middleware,” Submitted to the IEEE Communications Magazine, 1998.
[18] D. C. Schmidt, D. L. Levine, and S. Mungee, “The Design and Performance of Real-Time Object Request Brokers,” Computer Communications, vol. 21, pp. 294–324, Apr. 1998.
[19] D. C. Schmidt and T. Suda, “An Object-Oriented Framework for Dynamically Configuring Extensible Distributed Communication Systems,” IEE/BCS Distributed Systems Engineering Journal (Special Issue on Configurable Distributed Systems), vol. 2, pp. 280–293, December 1994.
[20] G. Blaine, M. Boyd, and S. Crider, “Project Spectrum: Scalable Bandwidth for the BJC Health System,” HIMSS, Health Care Communications, pp. 71–81, 1994.
[21] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, and M. Stal, Pattern-Oriented Software Architecture - A System of Patterns. Wiley and Sons, 1996.
本文轉載至ACE開發者
posted on 2007-02-27 21:47
walkspeed 閱讀(3585)
評論(0) 編輯 收藏 引用 所屬分類:
ACE Farmeworks