脈絡:應用程式正用訊息傳遞來接收訊息。
為什麼會收到說不通的訊息#
理論上通道上的一切都只是訊息,接收端就只是處理訊息。但要處理一則訊息,接收端必須能解讀它的資料、理解它的意義——而這不總是辦得到:
- 訊息內文可能造成剖析錯誤、語彙錯誤或驗證錯誤
- 訊息表頭可能缺少必要屬性,或屬性值說不通
- 寄件端可能把一則完全正常的訊息放到錯誤的通道上,於是送給了錯誤的接收端
- 惡意的寄件端可能故意送出不正確的訊息來搞亂接收端
Message Channel 應該是 Datatype Channel——通道上的每則訊息都該是該通道對應的正確資料型別。若寄件端放了型別不對的訊息,訊息系統仍會成功傳輸,但接收端認不得它,也不知道怎麼處理。
具體例子:
- 在應該裝文字訊息的通道上出現位元組訊息
- 格式不正確的訊息——例如格式不良(not well-formed)的 XML 文件,或不符合約定 DTD/schema 的文件
- 缺少接收端預期表頭欄位的訊息——例如少了 Correlation Identifier、Message Sequence 識別碼或 Return Address
就訊息系統而言這些訊息沒有任何問題,訊息也被正確遞送了;但接收端無法處理它們,所以它們是無效的。
幾個糟糕的處置方式#
接收端發現訊息無效時該怎麼辦?
- 放回通道——那它只會被同一個或同類的接收端再消費一次;同時這些被忽略的無效訊息會堵塞通道、傷害效能。
- 消費掉然後丟棄——這會掩蓋掉那些應該被察覺的訊息問題。
我們需要的,是把不正確的訊息從通道中清出來、放到不礙事的地方,但那個地方仍然能讓人偵測到它們,以診斷訊息系統的問題。
解法#
設計供應用程式使用的訊息系統時,管理員需要定義一個或多個無效訊息通道。
這個通道不會用於正常、成功的通訊,因此被不正確的訊息塞滿也不會造成問題。想診斷問題的錯誤處理器,可以在這個通道上放一個接收者,隨時偵測新出現的訊息。
它就是訊息傳遞的錯誤記錄檔#
Invalid Message Channel 就像訊息傳遞的 error log。應用程式出錯時該記錄錯誤;處理訊息出錯時,就該把訊息放到無效訊息通道上。
如果光看通道上的訊息還不能明顯看出它為何無效,應用程式也應該另外記錄一筆帶有更多細節的錯誤。
有效與無效是相對的#
- 對某個接收端有效的訊息,對另一個接收端可能無效;這兩個接收端就不該共用同一個通道。
- 對某通道上某個接收端有效的訊息,對該通道上所有其他接收端也應該有效;反之,若一個接收端認為某訊息無效,其他接收端也該如此認定。
- 寄件端有責任確保自己送上通道的訊息會被該通道的接收者視為有效,否則接收者只會把它們改道送往無效訊息通道,形同忽略。
訊息錯誤 vs. 應用程式錯誤#
有個相似但不同的問題:訊息結構正確,但內容在語意上是錯的。
例如一則 Command Message 指示接收端刪除一筆不存在的資料庫紀錄。這不是訊息錯誤,而是應用程式錯誤。雖然把它丟進無效訊息通道很誘人,但訊息本身沒問題,當成無效訊息處理會造成誤導——這種錯誤應該當作「無效的應用請求」來處理。
當接收端被實作成 Service Activator 或 Messaging Gateway 時,這個區分會變得更簡單清楚——這兩個模式把訊息處理程式碼與應用程式其餘部分分開:
- 錯誤發生在處理訊息時 → 訊息無效,移到 Invalid Message Channel
- 錯誤發生在應用程式處理訊息中的資料時 → 這是與訊息傳遞無關的應用程式錯誤
別讓它變成沒人看的日誌#
一個內容沒人理會的 Invalid Message Channel,和一份沒人看的錯誤記錄檔一樣沒用。
這個通道上的訊息代表應用整合出了問題,不該被忽略,而應被分析以找出癥結並修正。理想上這是一個自動化流程:消費無效訊息、判斷原因、修正底層問題。但癥結往往是編碼或設定錯誤,需要開發者或系統分析師評估與修復。
與死信通道的差別#
許多訊息系統實作了一個相近的概念 Dead Letter Channel。差別在於:
- Invalid Message Channel——訊息能被遞送與接收,但無法被處理
- Dead Letter Channel——訊息系統根本無法正確遞送該訊息
範例:股票交易與 JMS 規格的建議
股票交易#
在股票交易系統中,一個負責執行交易請求的應用程式,可能收到:
- 一則其實是「查詢當前報價」的請求
- 一則沒有指明要買哪支證券、或要買幾股的交易請求
- 一則沒有指明該把交易確認送給誰的交易請求
以上任一種情況,應用程式收到的都是無效訊息——不符合它處理交易請求所需的最低要求。判定訊息無效後,它應該把訊息重新送到 Invalid Message Channel 上。
那些送出交易請求的各個應用程式,可能會想監控這個無效訊息通道,以判斷自己的請求是否被丟棄了。
JMS 規格#
JMS 規格建議:若 MessageListener 收到無法處理的訊息,一個行為良好的 listener 應該把訊息轉往「某種應用程式專屬的『無法處理訊息』目的地」——那個目的地就是 Invalid Message Channel。
簡易訊息傳遞範例#
後續的 JMS 與 .NET 請求/回覆範例,會示範如何實作「把無法處理的訊息改道送往 Invalid Message Channel」的接收端。