脈絡:我的應用程式正用訊息傳遞執行 Request-Reply。

訊息未必彼此獨立#

訊息常被想成完全獨立的東西——任何寄件端隨時想往哪個通道送就往哪送。但訊息其實常常是相關聯的,例如 Request-Reply 這一對:兩則訊息看似獨立,但回覆訊息與引發它的請求訊息是一對一對應的

因此,處理請求的回覆方不能隨便挑一個通道送回覆——它必須送到請求方預期的那個通道上。

為什麼不能寫死#

  • 每個接收端當然可以「自動知道」該用哪個通道回覆,但把這種假設寫死會讓軟體缺乏彈性、更難維護
  • 而且單一個回覆方可能正在處理來自好幾個不同請求方的呼叫,回覆通道並非每則訊息都相同——它取決於是誰送來的請求。

情況還可能更複雜:

請求方未必希望回覆被送回自己身上。它可能有一個關聯的回呼處理器來處理回覆,而那個處理器監聽的通道可能與請求方監聽的不同(請求方甚至可能根本不監聽任何通道)。請求方也可能有多個回呼處理器,使得同一個請求方發出的不同請求,其回覆應該送往不同的處理器。

因此回覆通道不必然把回覆送回請求方,而是送給請求方希望由誰來處理回覆的那一方——因為它正在監聽請求方所指定的通道。

所以,知道是哪個請求方送出請求、或請求是走哪個通道來的,都不足以告訴回覆方該把回覆送到哪裡。就算足夠,回覆方仍得去推斷「這個請求方/這個請求通道該對應哪個回覆通道」。讓請求自己明確指定回覆通道,簡單得多。

解法#

如此一來,回覆方不需要知道該把回覆送到哪,它只要問請求就好。若送給同一個回覆方的不同訊息需要把回覆送往不同地方,回覆方也知道每個請求各該送去哪。

這把「請求與回覆各該用哪些通道」的知識封裝在請求方之內,這些決策就不必寫死在回覆方裡。

Return Address 放在訊息的表頭,因為它不是被傳輸資料的一部分。

一個好比喻#

訊息的 Return Address 類比於電子郵件中的 reply-to 欄位。這個「回覆至」位址通常與「寄件者」位址相同,但寄件者可以把它設成不同的位址,好在另一個帳號(而非寄信用的那個)收回覆。

與關聯識別碼的分工#

回覆沿著 Return Address 指定的通道送回時,可能還需要一個 Correlation Identifier

  • Return Address 告訴接收端該把回覆訊息放到哪個通道
  • Correlation Identifier 告訴寄件端這則回覆是對應哪一個請求
範例:JMS、.NET 與 Web Services

JMS 的 Reply-To 屬性#

JMS 訊息有一個預先定義好的 Return Address 屬性 JMSReplyTo。它的型別是 DestinationTopicQueue),而不只是一個目的地名稱字串——這確保了該目的地(也就是 Message Channel)真的存在,至少在請求送出的當下是如此。

寄件端指定一個 queue 作為回覆通道:

Queue requestQueue = // Specify the request destination
Queue replyQueue = // Specify the reply destination
Message requestMessage = // Create the request message
requestMessage.setJMSReplyTo(replyQueue);
MessageProducer requestSender = session.createProducer(requestQueue);
requestSender.send(requestMessage);

接收端接著這樣送出回覆:

Queue requestQueue = // Specify the request destination
MessageConsumer requestReceiver = session.createConsumer(requestQueue);
Message requestMessage = requestReceiver.receive();
Message replyMessage = // Create the reply message
Destination replyQueue = requestMessage.getJMSReplyTo();
MessageProducer replySender = session.createProducer(replyQueue);
replySender.send(replyMessage);

.NET 的 Response-Queue 屬性#

.NET 訊息同樣有預先定義好的 Return Address 屬性 ResponseQueue,型別是 MessageQueue——也就是應用程式該把回應訊息送去的那個佇列。

Web Services 的請求/回應#

SOAP 1.2 納入了 Request-Response Message Exchange Pattern,但**「回覆該送到哪個位址」並未指定,因此是隱含的**。

這個 SOAP 模式必須支援一個可選的 Return Address,才能真正讓 SOAP 訊息非同步、並把回應方與請求方解開。

新興的 WS-Addressing 標準有助於解決這個問題——它規定了如何識別一個 Web service 端點、以及該用哪些 XML 元素。這樣的位址就能放進 SOAP 訊息中作為 Return Address。