脈絡:應用程式需要喚起其他應用程式提供的功能。它通常會用遠端程序呼叫,但希望享有訊息傳遞的好處。

同步既是優點也是缺點#

遠端程序呼叫的優點是同步:呼叫立即執行,呼叫端的執行緒阻塞等待。

但這同時也是缺點。若此刻無法完成呼叫——網路不通,或遠端行程沒在跑、沒在監聽——那這次呼叫就是失敗。若呼叫是非同步的,它就能一直重試,直到遠端應用程式中的程序被成功喚起。

訊息傳遞是非同步的。若程序能以某種方式被一則 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 資料的驗證精確得多——因為方法元素型別可以指定參數的名稱、型別與順序。