脈絡:應用程式想把資料傳給另一個應用程式。它可以用檔案傳輸或共享資料庫,但這些作法各有不足;改用訊息傳遞或許更好。

其他作法為什麼不夠好#

這是分散式處理的經典問題:一個行程有另一個行程需要的資料。

  • 檔案傳輸好用,但協調應用程式的能力很差。一個應用程式寫出的檔案,可能擺很久才被另一個讀取;若有好幾個應用程式都該讀它,誰負責刪檔就變得不明確
  • 共享資料庫要求為這份資料在資料庫中新增 schema,或硬把資料塞進既有 schema。資料一旦進了資料庫,就有不該存取它的應用程式現在存取得到的風險;要觸發接收端「現在來讀」也不容易,而協調多個讀取者又會讓「誰該刪資料」變得混亂。
  • 遠端程序呼叫可以用來送資料,但呼叫端同時也透過所喚起的程序告訴接收端該拿資料做什麼
  • Command Message 同樣能傳輸資料,但它對「接收端該做什麼」規定得太具體了

我們確實想用訊息傳遞:它比 RPC 可靠;Point-to-Point Channel 能確保只有一個接收者拿到資料(不重複),Publish-Subscribe Channel 則能確保每個想要的接收者都拿到一份副本。

訣竅在於:享受訊息傳遞的好處,同時別讓這則訊息變得太像 RPC。

解法#

Command Message 告訴接收端去喚起某段行為;Document Message 則只是把資料交過去,讓接收端自己決定要不要處理、怎麼處理

這份資料是單一的資料單位——一個物件或資料結構,其內部或許還能再拆成更小的單位。

與事件訊息的差別#

Document Message 看起來可能很像 Event Message,主要差別在於時機與內容

Document Message 重要的是它的內容,也就是那份文件。成功傳輸文件才是重點,何時送出與收到相對不重要。

因此 Guaranteed Delivery 可能需要考慮,Message Expiration 大概不需要。

實作與搭配#

Document Message 可以是訊息系統中的任何一種訊息:

  • JMS 中可以是裝著可序列化資料物件的 ObjectMessage,或裝著 XML 形式資料的 TextMessage
  • .NET 中就是一則裝著資料的 Message
  • 一則 SOAP 回覆訊息就是一則文件訊息

搭配方式:

  • Document Message 通常用 Point-to-Point Channel 送出,好把文件從一個行程搬到另一個而不產生副本
  • 訊息傳遞可以用來實作簡單的工作流程:把文件交給某個應用程式修改,再交給下一個。
  • 有些情況下文件訊息可以透過 Publish-Subscribe Channel 廣播,但這會產生多份副本。

廣播文件訊息時,這些副本要嘛必須是唯讀的;否則若各接收端各自修改自己那份,系統中就會出現多份內容不同的同一文件

  • Request-Reply 中,回覆通常就是一則 Document Message,結果值即是那份文件。
範例:Java 與 XML、SOAP 與 WSDL

Java 與 XML#

以下範例展示如何把一張簡單的採購單表示成 XML,並用 JMS 當作訊息送出:

Session session = // Obtain the session
Destination dest = // Obtain the destination
MessageProducer sender = session.createProducer(dest);
String purchaseOrder =
"  <po id=\"48881\" submitted=\"2002-04-23\">
       <shipTo>
           <company>Chocoholics</company>
           <street>2112 North Street</street>
           <city>Cary</city>
           <state>NC</state>
           <postalCode>27522</postalCode>
       </shipTo>
       <order>
           <item sku=\"22211\" quantity=\"40\">
               <description>Bunny, Dark Chocolate, Large</description>
           </item>
       </order>
   </po>";
TextMessage message = session.createTextMessage();
message.setText(purchaseOrder);
sender.send(message);

SOAP 與 WSDL#

  • 使用文件風格的 SOAP 訊息時,整則 SOAP 訊息就是本模式的實例:訊息內文是一份 XML 文件(或某種被轉成 XML 的資料結構),SOAP 訊息把該文件從寄件端送到接收端。
  • 使用 RPC 風格的 SOAP 訊息時,回應訊息是本模式的實例:訊息內文含有被喚起方法的回傳值。

以下例子回傳喚起 GetLastTradePrice 方法的答案:

<SOAP-ENV:Envelope
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
   <SOAP-ENV:Body>
        <m:GetLastTradePriceResponse xmlns:m="Some-URI">
             <Price>34.5</Price>
        </m:GetLastTradePriceResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>