脈絡:在前面幾個模式的訂單處理例子中,目前無庫存的訂單項目可以由多家外部供應商之一供應。但供應商自己也未必有貨,報價可能不同、可供貨的日期也可能不同。
要以最好的方式滿足訂單,我們應該向所有供應商索取報價,再決定哪一家對這項商品提供最好的條件。
對解法的要求#
- 決定接收者的方式要有彈性——我們可以在中央決定核可供應商的清單,也可以讓任何有興趣的供應商參與「競標」。
因為我們對接收者沒有(或幾乎沒有)控制權,就必須準備好「只從一部分接收者收到回應」的情況。而競標規則的這類變動,不該衝擊方案的結構完整性。
- 要對後續處理隱藏個別接收者的數量與身分——把訊息的散發封裝在本地,能讓其他元件不受個別訊息路徑的影響。
- 要協調後續的訊息流。
最簡單的解法或許是讓每個接收者把回覆貼到一個通道上,交由後續元件自行處理這些個別訊息的調解。但這會要求後續元件知道「訊息被送給了多個接收者」這件事;而且後續元件在不了解已套用的路由邏輯的情況下,也更難處理這些個別訊息。
因此,把路由邏輯、接收者、以及個別訊息的後處理合併成一個邏輯元件是合理的。
解法#
Scatter-Gather 把請求訊息路由給多個接收者,接著用一個 Aggregator 收集回應並提煉成單一回應訊息。
兩種變體#
送出請求訊息的機制有兩種:
- 透過 Recipient List 散發——讓 Scatter-Gather 控制接收者清單,但要求它知道每個接收者的訊息通道。
- 拍賣式(Auction-style)——用 Publish-Subscribe Channel 把請求廣播給任何有興趣的參與者。這讓 Scatter-Gather 只需使用單一通道,但同時也放棄了控制權。
與組合式訊息處理器的關係#
這個解法與 Composed Message Processor 有相似之處:
- 不使用 Splitter,而是用 Publish-Subscribe Channel 把完整訊息廣播給所有相關方
- 多半會加上 Return Address,好讓所有回應都能透過單一通道處理
- 與 Composed Message Processor 一樣,回應依既定業務規則聚合——本例中 Aggregator 可能挑出「能滿足訂單的供應商中的最佳出價」
兩者都把單一訊息路由給多個接收者,再把個別回覆訊息合併回單一訊息,也都在執行「同步多個平行活動」的任務。
若個別活動所需時間差異極大,後續處理會被拖住——即使許多(甚至除了一個之外的所有)子任務都已完成。這一點必須與 Scatter-Gather 帶來的簡潔與封裝性一起權衡。
兩者之間的折衷方案是串接式(cascading)Aggregator——這種設計允許只拿到部分結果就啟動後續任務。
範例:Loan Broker 與模式的組合

圖 7-15:組合 Scatter-Gather 與 Composed Message Processor
Loan Broker#
Loan Broker 範例用 Scatter-Gather 把貸款報價請求路由給多家銀行,並從進站回應中選出最好的一筆。範例實作同時示範了基於 Recipient List 的方案(MSMQ 非同步實作)與基於 Publish-Subscribe Channel 的方案(TIBCO ActiveEnterprise 非同步實作)。
組合模式:把 Scatter-Gather 與 Composed Message Processor 疊起來#
我們現在可以用 Scatter-Gather 來實作 widget 與 gadget 的訂單處理,並把它與 Composed Message Processor 結合:
- 處理每張進站訂單,拆成個別項目
- 把每個項目送出去徵求出價
- 把每個項目的各家出價聚合成一則綜合出價回應
- 最後把所有出價回應聚合成一份完整報價
這是「多個整合模式如何組合成完整方案」的真實範例。把個別模式組合成更大的模式,讓我們能在更高的抽象層次上討論方案,也讓我們能修改實作細節而不影響其他元件。
這個例子同時展現了 Aggregator 的多樣性——方案用了兩個目的相當不同的 Aggregator:
- 第一個 Aggregator(Scatter-Gather 的一部分) 從多家廠商中選出最佳出價。它未必需要所有廠商都回應(速度可能比低價更重要),但可能需要複雜的合併演算法。
例如訂單要 100 個 widget,而報價最低的供應商只有 60 個庫存——Aggregator 必須能決定要不要接受這個報價、並從另一家補足剩下的 40 個。
- 第二個 Aggregator(Composed Message Processor 的一部分) 可能簡單得多,它只是把第一個 Aggregator 收到的所有回應串接起來。但它必須確保所有回應確實都收到了,並處理「某些項目沒有回應」這類錯誤狀況。