脈絡:應用程式需要喚起其他應用程式提供的功能。它通常會用遠端程序呼叫,但希望享有訊息傳遞的好處。
同步既是優點也是缺點#
遠端程序呼叫的優點是同步:呼叫立即執行,呼叫端的執行緒阻塞等待。
但這同時也是缺點。若此刻無法完成呼叫——網路不通,或遠端行程沒在跑、沒在監聽——那這次呼叫就是失敗。若呼叫是非同步的,它就能一直重試,直到遠端應用程式中的程序被成功喚起。
訊息傳遞是非同步的。若程序能以某種方式被一則 Message 喚起,那它甚至不必是遠端可存取的——它可以只是自己行程內的一個本地程序。問題於是變成:怎麼把一次程序呼叫變成一則訊息?
所幸「如何把一個請求封裝成物件」早有成熟的模式:Command 模式 [GoF] 說明如何把請求變成一個可被儲存與傳遞的物件。如果這個物件就是一則訊息,那它就能被存放在 Message Channel 中傳遞;命令的狀態(如果有的話)也能存進訊息的狀態裡。
解法#
命令沒有專屬的訊息型別——Command Message 只是一則「剛好裝著命令」的普通訊息:
- 在 JMS 中,它可以是任何型別的訊息——裝著可序列化命令物件的
ObjectMessage、裝著 XML 形式命令的TextMessage等等。 - 在 .NET 中,它就是一則裝著命令的
Message。 - 一則 SOAP 請求就是一則命令訊息。
Command Message 通常送在 Point-to-Point Channel 上,好讓每個命令只被消費與喚起一次。
範例:SOAP 與 WSDL
使用 RPC 風格的 SOAP 訊息時,請求訊息就是 Command Message 的實例:SOAP 訊息內文(一份 XML 文件)包含要在接收端喚起的方法名稱與傳入該方法的參數值。這個方法名稱必須與接收端 WSDL 中定義的某個 message 名稱相同。
以下 SOAP 規格中的例子,喚起接收端的 GetLastTradePrice 方法,並傳入單一個名為 symbol 的參數:
<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:GetLastTradePrice xmlns:m="Some-URI">
<symbol>DIS</symbol>
</m:GetLastTradePrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>在 SOAP 命令中,你可能會預期方法名稱是某個標準
<method>元素的值;實際上,方法名稱就是那個方法元素自己的名稱(前面加上m命名空間前綴)。為每個方法各設一種 XML 元素型別,能讓 XML 資料的驗證精確得多——因為方法元素型別可以指定參數的名稱、型別與順序。