物件導向(object orientation)自問世以來廣受歡迎,因為它天生適合把軟體切成定義清楚、彼此獨立的元件,讓開發者能專注於各自的功能。1980 年代起,物件導向開始被用於建構分散式系統:由遠端伺服器代管獨立物件、同時維持高度的分佈透明性(distribution transparency),成為新一代分散式系統的堅實基礎。

分散式物件#

物件的核心特徵是封裝:

  • 狀態(state):物件封裝的資料。
  • 方法(method):操作這些資料的程式碼,透過**介面(interface)**對外提供。
  • 行程(process)存取或操作物件狀態的唯一「合法」途徑,就是呼叫介面上的方法。
  • 一個物件可以實作多個介面;同一個介面定義也可以有多個物件提供實作。

介面與實作的嚴格分離對分散式系統至關重要:介面可以放在一台機器上,物件本體放在另一台機器上——這種組織方式就稱為分散式物件(distributed object)

用戶端繫結(bind)到分散式物件時的運作流程:

  • 一份介面實作——稱為代理(proxy)——被載入用戶端的位址空間,角色類似 RPC 系統的 client stub:把方法呼叫封送(marshal)成訊息、把回覆訊息解封送後回傳結果。
  • 實際物件位於伺服器機器上,提供與用戶端所見相同的介面。
  • 進入伺服器的呼叫請求先交給 server stub 解封送,再對物件介面發起方法呼叫;它也負責封送回覆並轉發給用戶端 proxy。
  • Server stub 常稱為骨架(skeleton),實務上往往是一個語言特定的不完整類別,需由開發者進一步特化。

圖 10-1:帶有用戶端代理(proxy)的遠端物件常見組織方式

多數分散式物件有個略違反直覺的特性:狀態本身並不分散,而是集中在單一機器上,只有介面被開放給其他機器使用。這種物件稱為遠端物件(remote object)。廣義的分散式物件中,狀態可以實體分散在多台機器上,但這種分佈同樣被介面隱藏起來。

編譯期物件 vs. 執行期物件#

  • 編譯期物件(compile-time object):直接對應語言層級的物件(Java、C++ 等),物件是類別(class)的實例。
    • 優點:大幅簡化分散式應用的開發。以 Java 為例,編譯類別定義即可產生實例化物件的程式碼,介面可編譯成 client/server 端 stub,開發者幾乎感覺不到物件的分佈。
    • 缺點:依賴特定程式語言
  • 執行期物件(runtime object):在執行期才明確建構出物件,與撰寫應用的語言無關,應用甚至可以由多種語言寫成的物件組成。
    • 實作方式完全開放,例如一個操作共用資料檔的 C 函式庫也能包成物件。
    • 常見手法是物件轉接器(object adapter):包在實作外層、唯一目的是讓它「看起來像物件」的包裝器。名稱源自 Gamma 等人的設計模式,用於把介面轉換成用戶端期望的形式。
    • 物件只以其實作的介面來定義;介面實作註冊到 adapter 之後,adapter 就能對外開放該介面供(遠端)呼叫,並負責執行呼叫請求。

持久性物件 vs. 暫時性物件#

  • 持久性物件(persistent object):不依附於當前伺服器行程而存在。伺服器可把物件狀態存到次級儲存後結束;之後新啟動的伺服器再把狀態讀回位址空間,繼續處理呼叫請求。
  • 暫時性物件(transient object):只存活於代管它的伺服器存續期間,伺服器一結束物件就消失。

是否需要持久性物件曾有不少爭論,有人認為暫時性物件就足夠。為了讓爭論不糾纏於 middleware 層,多數物件式分散式系統乾脆兩者都支援。

範例:Enterprise Java Beans#

Java 語言與其模型是眾多分散式系統的基礎,受歡迎的原因是直觀的物件導向支援加上內建的遠端方法呼叫,提供了很高的存取透明性。為了進一步簡化多層式(multitiered)client-server 應用的開發,發展出了 Enterprise Java Beans(EJB)

  • EJB 本質上是由特殊伺服器代管的 Java 物件,伺服器提供多種讓遠端用戶端呼叫該物件的方式。
  • 關鍵在於這個伺服器把應用功能與系統面功能分離:後者包括物件查找、物件儲存、交易參與等。
  • EJB 內嵌於**容器(container)**中,容器提供通往應用伺服器底層服務的介面,並可近乎自動地把 EJB 繫結到這些服務:遠端方法呼叫(RMI)、資料庫存取(JDBC)、命名(JNDI)、訊息傳遞(JMS)。

圖 10-2:EJB 伺服器的一般架構

程式設計者需要區分四種 EJB:

  1. 無狀態會期 bean(stateless session bean):暫時性物件,被呼叫一次、完成工作後即丟棄所有資訊。例如「列出暢銷書排行」的服務:對資料庫下 SQL 查詢、把結果整理成用戶端可用的格式,然後結束。
  2. 有狀態會期 bean(stateful session bean):維護與用戶端相關的狀態。典型例子是電子商務的購物車:加入、移除商品、結帳,期間查詢資料庫取得價格與庫存。生命期仍有限——用戶端結束後(可能呼叫多次)bean 就自動銷毀,所以仍稱為 session bean。
  3. 實體 bean(entity bean):長壽命的持久性物件,通常存放在資料庫中,也常參與分散式交易。典型用途是記錄客戶資訊(收件地址、帳單地址、信用卡資料等),用戶端登入時還原對應的 entity bean 繼續使用。
  4. 訊息驅動 bean(message-driven bean):用來撰寫對進入訊息作出反應的物件,不能被用戶端直接呼叫,而是走發佈/訂閱(publish-subscribe)式通訊——伺服器收到先前訂閱的特定訊息時自動呼叫 bean,處理完即丟棄,因此屬於無狀態。

範例:Globe 分散式共享物件#

Globe 是一個以**可擴充性(scalability)**為核心的物件式分散式系統,整體設計圍繞著支撐大規模廣域系統、海量使用者與物件的需求。

與其他物件式系統的最大差異在於:Globe 的物件除了封裝狀態與操作外,還封裝了規範狀態如何跨機器分佈的政策實作

  • 每個物件自行決定狀態如何分佈到各副本(replica)。
  • 物件決定狀態於何時、如何、遷移到哪裡。
  • 物件決定是否複製、以及如何複製。
  • 物件甚至可以自訂安全政策與其實作。

物件模型#

Globe 不採用遠端物件模型,物件可以實體分散:狀態分佈並複製於多個行程之間,因此稱為分散式共享物件(distributed shared object)——物件通常由多個行程共享。此模型源自 Orca 的分散式物件,類似做法還有 fragmented objects。

  • 繫結到分散式共享物件的行程,會取得該物件介面的一份本地實作,稱為本地代表(local representative)本地物件(local object)
  • 本地物件是否含有狀態,對繫結的行程完全透明;所有實作細節都藏在介面之後,外部只看得到方法。

圖 10-3:Globe 分散式共享物件的組織方式

本地物件分兩類:基本(primitive)本地物件不包含其他本地物件;複合(composite)本地物件由多個(可能也是複合的)本地物件組成。實作分散式共享物件所需的本地物件即為複合物件,至少包含四個子物件:

  • 語意子物件(semantics subobject):實作分散式共享物件的實際功能,本質上對應一般的遠端物件,風格類似 EJB。
  • 通訊子物件(communication subobject):提供通往底層網路的標準介面,含連線導向與非連線式的訊息傳遞原語;另有支援多播(multicasting)、可靠或不可靠通訊的進階版本。
  • 複製子物件(replication subobject):實作物件的實際分佈策略,介面同樣標準化。它決定語意子物件的方法何時真正執行——例如實作主動式複製(active replication)的複製子物件,會確保所有方法呼叫在每個副本上以相同順序執行,為此需與組成同一分散式共享物件的其他本地物件的複製子物件通訊。
  • 控制子物件(control subobject):介於語意子物件的使用者自訂介面與複製子物件的標準化介面之間,並負責把語意子物件的介面匯出給繫結的行程。行程發出的方法呼叫由控制子物件封送後交給複製子物件;複製子物件放行後,控制子物件才執行呼叫並回傳結果。來自遠端行程的呼叫請求同樣交由控制子物件解封送、執行、把結果交回複製子物件。

圖 10-4:用於分散式共享物件的本地物件之一般組織方式

Globe 的設計哲學是「盡可能讓物件自己作主」:分佈、複製、遷移、安全等政策全部封裝進物件本身,而不是交給系統統一決定。這與把物件視為單機遠端物件的 CORBA/Java 形成鮮明對比。