第5章分佈式系統模式

在當今的互聯世界中,越來越多的企業應用程序跨多個服務器分佈和運行、連接到遠程數據源和 Web Service,並可通過 Internet 訪問。分佈式計算功能強大,但也並非沒有面臨挑戰。網絡在本質上並不可靠,同本地的進程間通信相比,與遠程服務器的通信速度較慢。另外,同時在多臺計算機 上運行一個程序可能會導致許多併發和同步問題。

基於實例的協作和基於服務的協議

按照 Business Component Factory(業務組件工廠)的說,分佈式計算可以基於兩個截然不同的體系結構樣式:

  • 基於實例的協作

  • 基於服務的協作

基於實例的協作跨網絡邊界擴展了面向對象的計算模型。組件可以實例化遠程對象實例、四處傳遞對這些遠程對象的引用、調用遠程對象的方法以及取消對它 們的分配。此方法的優點在於,可以將應用程序內使用的同一個面向對象的編程模型應用於分佈式組件。大多數運行時平臺都包含對基於實例的協作的支持,以便開 發人員 在訪問遠程對象時不必提供特殊內容,或者相對於訪問本地對象,只需提供極少的特殊內容。這就大大簡化了分佈式解決方案的開發,通常會簡化到如下程度:以前 位於相同位置的對象可以在部署時進行分佈,而無需對應用程序進行任何代碼更改。基於實例的協作還使遠程對象的使用者能夠更加精細地控制遠程對象的生存期, 從而讓用戶更有效地使用遠程資源。

但是,基於實例的協作在帶來易用性的同時,還因複雜的交互模型以及用戶和提供商之間的緊密聯繫而導致成本增加。基於實例的交互要求遠程對象的特定實 例可以通過網絡找到,這將複雜的生存期和實例管理引入了通信協議中。因此,大多數支持基於實例的協作的平臺都 不提供與其他平臺的互操作性。

基於服務的協作通過只向潛在使用者公開“類似於管理器”或“類似於協調器”的接口來解決其中的一些挑戰。用戶可以調用該接口上的方法,但是他們不能控制任何遠程對象的生存期。這大大簡化了交互,並允許使用支持跨平臺互操作的標準協議。

但是,基於服務的協作不能爲本地對象和遠程對象提供使用面向對象的編程模型時所具備的連續性。這意味着您必須明確跟蹤對象之間的會話狀態,而在使用 基於實例的協作時就不必擔心這些。同樣,儘管基於標準的協議改善了互操作性,但是它們要求應用程序將應用程序內部數據類型轉換成每個通信終端都能夠理解的 通用格式,而這可能涉及其他轉換邏輯。

近鏈接與遠鏈接

考慮分佈式系統的另一種方式就是將每個系統視爲通過鏈接連在一起的處理節點的集合。這些節點代表實際的服務器計算機,而鏈接代表將這些計算機連在一起的網絡。系統內的鏈接分成兩類:近鏈接或遠鏈接。

近 鏈接駐留在同一個信任區域中、同一個企業內,它們以可靠方式連接而且不涉及互操作性。遠鏈接包括所有其他鏈接(包括跨越 Internet 的任何鏈接)。 如果您的分佈式系統僅跨越近鏈接,則最好使用基於實例的協作。使用基於實例的協作,可以跨計算機邊界擴展面向對象的開發能力,同時利用平臺的基礎結構來優 化速度、導航類型系統併爲您操作封送處理細節。此處的技術選項將包括 Microsoft? .NET Framework 中的 .NET Remoting 和 Enterprise Services。

另一方面,如果您的分佈式系統跨越遠鏈接,則基於服務的協作通常是較好 的選擇。如果與提供“類似於協調器”的接口的服務進行交互,則允許該服務負責實現,而不必讓服務用戶瞭解實現細節。服務接口通常返回消息,這比遠程過程調 用提供的耦合性要少。最好的消息是那些既包含頭信息又包括正文的消息,這允許接收者針對收到的消息自發執行操作。此處的技術選項包括諸如 Web Services 這樣的功能。

本章的其餘部分將描述通常與基於實例的協作和近鏈接相關聯的模式。第 6 章“服務模式”將進一步介紹通常與基於服務的協作和遠鏈接相關聯的模式。

分佈式計算所面臨的挑戰

分佈式體系結構的核心是能夠調用對象上的方法或者與駐留在不同進程中甚至有可能在不同計算機上的服務進行通信。看起來這似乎不難,但是您必須解決非常多的問題:

  • 如何實例化遠程對象?

  • 如果您希望調用現有對象上的方法,那麼如何獲取對該對象的引用?

  • 網絡協議僅傳輸字節流,而不傳輸對象。如何才能通過字節流調用方法?

  • 安全性如何?是否每個人都能夠調用遠程對象上的方法?

  • 大多數網絡在本質上都不可靠。如果無法訪問遠程對象,會出現什麼情況?如果遠程對象收到方法調用,但是因網絡問題而無法發送響應,該怎麼辦?

  • 調用遠程對象比調用本地方法慢得多。您是否希望異步調用遠程方法,以便在遠程對象處理請求的同時可以繼續在本地處理?

問 題不勝枚舉。幸運的是,.NET Framework 中的功能解決了其中的大多數問題,使得開發人員在創建分佈式應用程序時不必處理太多的細枝末節。這些功能使得遠程調用對於程序員幾乎是透明的(至少在句法 級別是如此)。但是,這種簡化可能存在一定的欺騙性,因爲開發人員還必須瞭解遠程通信的一些基本原理,才能寫出強大而高效的分佈式應用程序。分佈式系統模 式羣集有助於開發人員在實現分佈式應用程序時作出明智的設計決策。

使用分層應用程序

爲分佈式系統創建易用的基礎結構的祕訣是Layered Application 。分佈式服務層依賴較低層(如 TCP/IP 堆棧和套接字通信層),但是較低層的細節不會顯示在包含應用程序和業務邏輯層的較高層中。這種安排使得應用程序開發人員在較高抽象級別工作時不必考慮諸如 TCP/IP 數據包和網絡字節排序之類的細節。它還可以在替換較低層時不對較高層造成任何影響。例如,您不必在應用程序層更改代碼,即可切換到另一個傳輸協議(例如, 使用 HTTP 代替直接使用 TCP/IP)。

便於開發人員進行遠程調用的一種方法就是使用 Proxy(代理)[Gamma95]。代理是與客戶端對象通信的本地替身。當客戶端創建遠程對象的實例時,基礎結構就會創建一個代理對象,該對象在客戶 端看來與遠程類型完全相同。當客戶端調用該代理對象上的方法時,該代理就會調用遠程處理基礎結構。遠程處理基礎結構將請求路由到服務器進程,然後調用服務 器對象,並將結果返回給客戶端代理,最後客戶端代理將結果傳遞給客戶端對象。由於所有這些操作都是在後臺進行的,因此客戶端對象可能完全不知道另一個對象 駐留在其他計算機上。這不僅方便了分佈式應用程序的開發,而且還允許您在程序開發出來之後再分佈對象,而只需對應用程序代碼進行很小的改動。

模式概述

分佈式系統模式羣集強調兩個主要概念:遠程調用和粗粒度接口。

圖 1 分佈式系統羣集中的模式

遠程調用

Broker (代理程序)模式描述如何查找遠程對象並調用它的一個方法,而不會將通過網絡進行通信的複雜性引入應用程序。此模式爲大多數分佈式體系結構(包括 .NET Remoting)奠定了基礎。

.NET Framework 的指導原則之一就是簡化複雜的編程任務,同時不會奪走程序員的控制權。按照該原則,.NET Remoting 提供了大量遠程處理模型供開發人員選擇,如下面的段落所述。

本地副本

最簡單的遠程處理模型涉及到按值將對象的副本傳遞給客戶端。以後針對該對象進行的所有方法調用都是真正的本地調用。此模型避免了分佈式計算中固有的 許多複雜 性,但同時也存在許多缺點。首先,由於您是在自己的進程空間中運行對象的本地副本,因此計算不是真正的分佈式計算。其次,由於對對象狀態進行的所有更新僅 發生在本地,因此它們會丟失。最後,由於對象需要遠程資源或者遠程對象的提供商希望保護對其內部資源的訪問,因此對象通常是遠程的。將對象實例複製到本地 進程不僅無法實現這兩個目標,而且還會增加因通過遠程通道傳送整個對象而帶來的開銷。由於存在上述限制,因此本章僅討論對象複製的唯一應用:Data Transfer Object (數據傳輸對象)模式。

服務器激活的對象

與使用遠程對象的本地副本相比,直接調用遠程對象上的方法是更好的選擇。但是,只有當您擁有對遠程對象的引用時,才能調用其上的方法。獲取對遠程對 象的引用 要求首先實例化該對象。客戶端要求服務器提供該對象的實例,然後服務器返回對遠程實例的引用。如果遠程對象可被視爲服務,則上述過程可以順暢地工作。例 如,假設有一個用來驗證信用卡號的服務。客戶端對象提交一個信用卡號並收到一個正的或負的響應,具體情況取決於客戶的消費(和償還)習慣。在本例中,您並 不真正關心遠程對象的實例。您只管提交某些數據、接收結果並繼續操作。這是“無狀態服務”的一個很好的例子。在無狀態服務中,每個請求都會使對象繼續保持 此前所處的狀態。

但是,並非所有遠程對象協作都遵循此模型。有時,您希望調用遠程對象,以便檢索某些可在以後進行遠程調用時訪問的數據。 您必須確保在隨後的調用過程中調用的是同一個對象實例。而且,當您檢查完數據後,您將希望取消對該對象的分配以節省服務器內存。利用服務器激活的對象,無 法對對象實例實現這種級別的控制。服務器激活的對象爲實例生存期管理僅提供兩個替換選項:

  • 針對每次調用創建對象的新實例。

  • 對於所有客戶端僅使用遠程對象的單個實例(使該對象有效地成爲 Singleton)。

如果您希望通過幾次功能調用來訪問同一遠程實例,然後將其扔到垃圾回收器中,則上述任一選項都不合適。

客戶端激活的對象

客戶端激活的對象使 客戶端能夠控制遠程對象的生存期。客戶端幾乎可以像實例化本地對象那樣實例化遠程對象,在客戶端刪除對該對象實例的所有引用之後,垃圾回收器會刪除遠程對 象。但是,這種級別的控制成本較高。要使用客戶端激活功能,必須複製可由客戶端進程訪問的程序集。這與各種客戶端應該無需進一步設置即可訪問遠程對象的想 法存在衝突。

但是,您可以通過創建一個服務器激活的對象(作爲服務器對象的工廠對象)來達到最佳平衡。此工廠對象創建其他對象的實例。工 廠本身是沒有狀態的,因此,您可以很方便地將它作爲服務器激活的 Singleton 來實現它。隨後,所有客戶端請求都共享該工廠的同一個實例。因爲該工廠對象在遠程運行,所以它所實例化的所有對象都是遠程對象,但是客戶端可以決定在何時 以及在何處實例化它們。

粗粒度接口

跨進程和網絡邊界調用方法比調用同一操作系統進程中對象上的方法慢得多。

許 多面向對象的設計做法通常傾向於設計帶有細粒度接口的對象。這些對象可以擁有許多帶有相關 getter 和 setter 的字段以及大量方法,每種方法都封裝了一小塊粘合在一起的功能。由於這種細粒度特性,所以必須調用大量方法才能實現所需的結果。因爲這種細粒度接口方法支 持許多令人滿意的應用程序特徵(如可維護性、可重複使用性和可測試性),所以對於獨立應用程序,這是一種理想選擇。

如果使用公開細粒度接 口的對象,則會大大影響應用程序的性能,這是由於細粒度接口要求跨進程和網絡邊界進行多次方法調用。爲了改善性能,遠程對象必須公開一個粒度更大的接口。 粗粒度接口公開一組相對較小的獨立方法。每種方法通常都代表一段高級功能(如下訂單或更新客戶)。因爲某個方法所需的全部數據都以參數形式傳入該方法中, 所以這些方法都被視爲獨立方法。

Data Transfer Object

The Data Transfer Object 模式將粗粒度接口概念應用於如下問題:在由進程和網絡邊界隔開的組件之間傳遞數據。它建議將許多參數替換爲一個對象,在該對象中存儲遠程方法所需的全部數據。該技術對於遠程方法返回的數據也非常適用。

有 多種選項可以用來實現數據傳輸對象 (DTO)。一種方法是爲解決方案所需的每種不同類型的 DTO 分別定義一個單獨的類。針對所包含的每種數據元素,這些類通常有一個強類型的公共字段(或屬性)。爲了跨網絡或進程邊界傳輸這些對象,這些類都要序列化。 序列化對象被跨邊界封送處理,然後在接收端重新建立。此方法的主要優點是性能和類型安全。此方法的封送處理開銷最小,DTO 的強類型字段確保類型錯誤在編譯時(而非運行時)即可被捕獲。此方法的缺點是需要爲每個 DTO 創建一個新類。如果解決方案需要大量 DTO,則與編寫和維護這些類相關的工作會非常艱鉅。

創建 DTO 的第二種方法是使用一般容器類來保存數據。此方法的常見實現是將類似於 ADO.NET DataSet 的類用作一般容器類。此方法需要兩次額外轉換。第一次轉換髮生在發送端,它將應用程序數據轉換爲適用於DataSet 的形式。第二次轉換髮生在接收端,它將數據從DataSet 中提取出來以用於客戶端應用程序。在某些應用程序中,這些額外的轉換可能會影響性能。此方法的另一個不足之處是缺乏類型安全性。如果將客戶對象放在發送端 的 DataSet 中,則在接收端上嘗試提取順序對象時會導致運行時錯誤。此方法的主要優點是不必編寫、測試或維護任何額外的類。

ADO.NET 提供了第三個可選選項 - 類型化 DataSet。ADO.NET 提供一種自動生成包裝 DataSet 的類型安全包裝器的機制。此方法存在與DataSet 方法同樣的潛在性能問題,但是應用程序可以受益於類型安全這一優點,並且開發人員不必爲每個 DTO 都開發、測試和維護一個單獨的類。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章