脈絡:我的應用程式正用訊息傳遞執行 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。它的型別是 Destination(Topic 或 Queue),而不只是一個目的地名稱字串——這確保了該目的地(也就是 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。