脈絡:應用程式想把資料傳給另一個應用程式。它可以用檔案傳輸或共享資料庫,但這些作法各有不足;改用訊息傳遞或許更好。
其他作法為什麼不夠好#
這是分散式處理的經典問題:一個行程有另一個行程需要的資料。
- 檔案傳輸好用,但協調應用程式的能力很差。一個應用程式寫出的檔案,可能擺很久才被另一個讀取;若有好幾個應用程式都該讀它,誰負責刪檔就變得不明確。
- 共享資料庫要求為這份資料在資料庫中新增 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>