脈絡:企業有兩套獨立的應用程式,正透過連接它們的 Message Channel 以訊息傳遞溝通。

通道傳的是「單位」,不是連續水流#

Message Channel 常被想成一根管子——一條從一個應用程式通往另一個的導管。照這個比喻,資料似乎可以像水一樣從一端灌進去、從另一端流出來。

資料不是連續的水流,它是一個個單位:紀錄、物件、資料庫列等等。所以通道傳輸的必須是資料單位

「傳輸」資料是什麼意思#

  • 在函式呼叫中,呼叫端可以傳指標(記憶體位址)來以參考傳遞參數——這行得通是因為呼叫端與函式共享同一個記憶體堆積。
  • 同一行程中的兩條執行緒也能靠傳指標交換紀錄或物件,因為它們共享同一個記憶體空間。
  • 兩個各自獨立的行程就得多做很多事:它們各有自己的記憶體空間,因此必須把資料從一個空間複製到另一個。

資料通常以位元組流這種最基本的形式傳輸,這意味著第一個行程必須把資料**封送(marshal)成位元組形式、複製到第二個行程,再由後者解封送(unmarshal)**還原成原本的形式——也就是第一個行程中那份資料的副本。

封送正是 RPC 把參數送給遠端行程、以及遠端行程回傳結果的方式。

所以訊息傳遞傳輸的是離散的資料單位,作法是在寄件端封送、在收件端解封送,讓收件端擁有自己的本地副本。我們需要的,是一種簡單的方式把一份資料單位包起來,讓它適合在訊息通道上傳輸。

解法#

因此,任何要透過訊息系統傳輸的資料,都必須被轉換成一或多則能送上通道的訊息。

一則訊息由兩個基本部分組成:

  1. 表頭(Header)——訊息系統使用的資訊,描述所傳輸的資料、它的來源、目的地等等。
  2. 內文(Body)——被傳輸的資料本身;訊息系統一般不理會它,原樣傳送。

這個概念並非訊息傳遞獨有:郵政信件與電子郵件都以離散的信件傳送資料;乙太網路以封包傳輸資料,TCP/IP 的 IP 層(例如網際網路)也是;網路上的串流媒體實際上就是一連串封包。

訊息的種類:系統看來一樣,開發者看來不同#

對訊息系統來說,所有訊息都一樣:一坨依表頭描述傳輸的資料。但對應用程式設計師而言,訊息有不同類型,也就是不同的使用風格:

  • 要喚起另一個應用程式中的某個程序 → Command Message(命令訊息)
  • 要把一組資料傳給另一個應用程式 → Document Message(文件訊息)
  • 要通知另一個應用程式「本應用程式發生了變化」→ Event Message(事件訊息)
  • 若對方應該回覆 → Request-Reply(請求-回覆)

其他相關考量:

  • 若要送的資訊超過單一訊息能承載的量,就把資料拆小,以 Message Sequence(訊息序列) 送出。
  • 若資料只在有限時間內有用,就用 Message Expiration(訊息過期) 指定它的使用期限。
  • 因為所有收發雙方都必須對訊息中的資料格式有共識,就用 Canonical Data Model(正規資料模型) 來指定格式。
範例:JMS、.NET 與 SOAP 中的訊息

JMS Message#

在 JMS 中,訊息以 Message 型別表示,底下有數個子型別。每個子型別的表頭結構相同,差別在內文格式

  • TextMessage——最常見的類型。內文是 String,例如文字檔或 XML 文件。textMessage.getText() 回傳 String
  • BytesMessage——最簡單、最通用的類型。內文是位元組陣列,bytesMessage.readBytes(byteArray) 把內容複製進指定陣列。
  • ObjectMessage——內文是單一 Java 物件,且必須實作 java.io.Serializable(以便封送與解封送)。objectMessage.getObject() 回傳該 Serializable
  • StreamMessage——內文是 Java 基本型別的串流,接收端用 readBoolean()readChar()readDouble() 之類的方法讀取。
  • MapMessage——內文的行為像 java.util.Map,鍵為 String。接收端用 getBoolean("isEnabled")getInt("numberOfItems") 之類的方法讀取。

.NET Message#

在 .NET 中由 Message 類別實作訊息型別:

  • Body 屬性以 Object 承載訊息內容
  • BodyStreamStream 儲存內容
  • BodyType 是一個 int,指出內文所含資料的型別(字串、日期、貨幣或數字等)

SOAP Message#

在 SOAP 協定 [SOAP 1.1] 中,一則 SOAP 訊息正是本模式的一個實例:它是一份 XML 文件,以信封(根元素 SOAP-ENV:Envelope)包住可選的表頭(SOAP-ENV:Header)與必要的內文(SOAP-ENV:Body)。這份 XML 文件是一筆可被傳輸的原子性資料紀錄(傳輸協定通常是 HTTP),所以它是一則訊息。

<SOAP-ENV:Envelope
 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
 SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
  <SOAP-ENV:Header>
      <t:Transaction
          xmlns:t="some-URI"
          SOAP-ENV:mustUnderstand="1">
              5
      </t:Transaction>
  </SOAP-ENV:Header>
  <SOAP-ENV:Body>
      <m:GetLastTradePrice xmlns:m="Some-URI">
          <symbol>DEF</symbol>
      </m:GetLastTradePrice>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

SOAP 也展示了訊息的遞迴本質:一則 SOAP 訊息可以透過訊息系統傳輸,這意味著一則訊息系統的訊息(例如 javax.jms.MessageSystem.Messaging.Message包著那則 SOAP 訊息(那份 SOAP-ENV:Envelope XML 文件)。在這種情境下,傳輸協定不是 HTTP,而是訊息系統本身——訊息系統或許在底層仍用 HTTP 或其他網路協定搬資料,但它讓這趟傳輸變得可靠