分散式策略#

物件導向技術問世以來,人們一直渴望將物件分散部署到不同的節點上。然而,物件的分散式部署遠比多數人想像的更加充滿陷阱,尤其是在廠商宣傳手冊的影響下,許多開發者往往忽略了其中的代價。本章正是關於這些慘痛教訓的探討。


The Allure of Distributed Objects#

分散式物件的誘惑#

作者描述了一個他在設計審查中經常遇到的場景:系統架構師自豪地展示一個將不同遠端物件(如客戶、訂單、產品、配送)分別放置在不同處理節點上的設計,聲稱這樣做是為了「效能」——當某個元件過於忙碌時,可以為它增加更多伺服器來實現負載平衡。

然而,這種架構實際上存在嚴重問題:

  • 透明性(Transparency) 雖然是有價值的概念,但效能絕非其中可以被透明化的部分
  • 以類別為基礎來分配分散式策略,會導致大量的遠端呼叫與笨拙的粗粒度介面
  • 即使為每個可遠端化的類別都加上粗粒度介面,最終仍會有過多的遠端呼叫,且系統難以修改

Figure 7.1: Distribute an application by putting different components on different nodes

分散式物件設計第一定律(First Law of Distributed Object Design):不要分散你的物件!

那麼,如何有效利用多處理器?在大多數情況下,正確的做法是叢集(Clustering):將所有類別放在單一行程中,然後在多個節點上運行該行程的多個副本。這樣每個行程都使用本地呼叫,效能更快,也能使用細粒度介面,並獲得更好的可維護性與更簡單的程式設計模型。

Figure 7.2: Clustering involves putting several copies of the same application on different nodes


Remote and Local Interfaces#

遠端介面與本地介面#

分散式物件設計之所以不可行,根本原因在於一個基本事實:

  • 行程內的程序呼叫非常快速
  • 跨行程的程序呼叫慢了好幾個數量級
  • 跨機器的呼叫還會再慢一到兩個數量級

因此,遠端使用的物件介面必須不同於本地使用的物件介面

本地介面 vs. 遠端介面的設計差異#

  • 本地介面最適合採用細粒度(fine-grained) 設計——例如一個地址類別會有分別取得城市、州、郵遞區號的方法。細粒度介面遵循 OO 原則,便於組合與擴展。
  • 遠端介面必須採用粗粒度(coarse-grained) 設計——一次呼叫就取得或更新城市、州、郵遞區號。這種設計並非為了彈性,而是為了最小化呼叫次數

廠商常聲稱其中介軟體在本地呼叫時沒有額外開銷,但這並不能迴避一個根本問題:任何可能被遠端使用的物件都需要粗粒度介面,這會讓程式設計變得笨拙。你無法直接將單一行程中設計好的類別群組,加上 CORBA 等中介軟體,就變成分散式模型。


Where You Have to Distribute#

必須分散的場景#

儘管應該盡量減少分散邊界,並透過叢集來充分利用節點,但仍有些地方不得不進行行程分離:

  • 客戶端與伺服器之間:使用者桌面上的 PC 與共享資料儲存庫是不同的機器,需要透過分離的行程來通訊。客戶端-伺服器的分隔是典型的跨行程邊界。
  • 應用伺服器與資料庫之間:雖然可以將應用軟體以預存程序的形式在資料庫行程中執行,但通常不太實際。一旦有分離的行程,就需要付出遠端呼叫的代價。幸運的是 SQL 本身就是為遠端介面設計的。
  • Web 伺服器與應用伺服器之間:理想情況下應在同一行程中運行,但並非總是如此。
  • 因廠商差異而必須分離:使用第三方套件時,它往往運行在自己的行程中。
  • 確實需要拆分應用伺服器軟體的情況:這種情況確實存在,但應該像賣祖母一樣不情願地去做

總體原則如 Colleen Roe 所說:「對物件分散要吝嗇到極點。」能避免就避免。


Working with the Distribution Boundary#

處理分散邊界#

設計系統時應盡可能限制分散邊界,但在必要之處,需要妥善處理:

  • 每次遠端呼叫都像「馬車運輸」般昂貴,系統各處都會為了最小化遠端呼叫而改變形態
  • 然而,單一行程內仍可使用細粒度物件進行設計

Remote Facade 模式#

關鍵做法是在分散邊界處放置粗粒度物件,作為內部細粒度物件的外觀(facade):

  • 粗粒度物件本身不做什麼實際工作,只是為細粒度物件提供遠端介面
  • 這個外觀僅為分散目的而存在,因此稱為 Remote Facade(遠端外觀)
  • 使用 Remote Facade 可將粗粒度介面帶來的困難降到最低——只有真正需要遠端服務的物件才有粗粒度方法

Data Transfer Object 模式#

Remote Facade 搭配使用的是 Data Transfer Object(資料傳輸物件):

  • 粗粒度方法不僅需要接收粗粒度的資料,還需要以一個區塊傳送這些資訊
  • 通常不能直接傳送領域物件本身(因為它綁定了大量細粒度的本地物件間參照)
  • 因此需要將客戶端所需的資料打包到一個專門的物件中——即 Data Transfer Object
  • Data Transfer Object 出現在連線的兩端,它只參照其他 Data Transfer Object 和基本型別(如字串),不參照任何未被共享的東西

另一種分散方式是使用 broker 在行程間遷移物件,利用類似 Lazy Load 的機制,不從資料庫而是跨網路移動物件。這種做法的困難在於確保不會因此產生大量遠端呼叫。


Interfaces for Distribution#

分散的介面選擇#

傳統上,分散式元件的介面基於遠端程序呼叫(RPC),以全域程序或物件上的方法形式呈現。近年來開始出現基於 XML over HTTP 的介面,其中 SOAP 可能是最常見的形式。

XML/HTTP 介面的優勢#

  • 可以在單次往返中傳送大量結構化資料,減少遠端呼叫次數
  • XML 是通用格式,各平台都有解析器,適合不同平台間的通訊
  • HTTP 近乎通用,且容易穿越防火牆
  • XML 的文字特性使除錯更為容易

何時選擇哪種介面#

  • 如果連線兩端使用相同平台,使用該平台內建的遠端呼叫機制更有效率——XML 介面在此情況下只是多了一組花俏的縮寫
  • Web 服務在不同平台需要互相溝通時最為實用
  • 可以在物件導向介面之上疊加 HTTP 介面,讓 Web 伺服器的呼叫轉譯為對底層 OO 介面的呼叫

作者偏好非同步的、基於訊息的方式來處理分散式系統,而非同步的 RPC 式呼叫。非同步訊息模式的設計模式是一個獨立的大主題。特別是 Web 服務,作者認為其最佳用法是非同步與訊息導向的,儘管當時大多數已發布的範例都是同步的。