脈絡:應用程式需要消費訊息,但它想控制自己何時消費每一則訊息

輪詢的美妙之處#

訊息消費者存在的理由只有一個——消費訊息。訊息代表待完成的工作,消費者得消費它們並把工作做完。

但消費者怎麼知道有新訊息可用?最容易的作法是反覆檢查通道:有訊息就消費它,然後回頭檢查下一則。這個過程就叫輪詢(polling)

輪詢的美妙之處在於:消費者是在自己準備好要處理下一則訊息時才去要它——因此它以自己想要的速率消費訊息,而不是以訊息抵達通道的速率

解法#

它也被稱為同步接收端,因為接收執行緒會阻塞直到收到訊息。之所以叫「輪詢式消費者」,是因為接收端輪詢訊息、處理它、再輪詢下一則

訊息 API 通常同時提供:

  • 一個阻塞到訊息送達receive 方法
  • 以及 receiveNoWait()Receive(0) 這類沒有訊息就立刻返回的方法

Polling Consumer 是應用程式用來明確索取訊息的物件:應用程式準備好要下一則訊息時就去輪詢它,由它從訊息系統取得訊息並回傳。

流程是:應用程式輪詢 → 消費者阻塞直到取得訊息(或滿足其他條件,例如時限)→ 應用程式處理訊息 → 處理完想要下一則就再輪詢。

節流#

使用輪詢式消費者,應用程式能靠限制輪詢執行緒的數量來控制同時消費多少訊息。這能避免接收端被過多請求淹沒——多出來的訊息會排隊等它處理得完

執行緒配置#

接收端應用程式通常每個想監控的通道至少用一條執行緒,但也可以用單一執行緒監控多個通道,在通道經常是空的時候節省執行緒

  • 輪詢單一通道(且執行緒在訊息抵達前無事可做)→ 用會阻塞receive
  • 用單一執行緒輪詢多個通道,或想在等待時做別的事 → 用帶逾時receivereceiveNoWait(),這樣某個通道空的時候,執行緒就能去檢查另一個通道或做別的工作

與其他模式的搭配#

  • 多個 Polling Consumer 可以成為 Competing Consumers
  • Message Dispatcher 可以實作成 Polling Consumer
  • Polling Consumer 可以同時是 Selective Consumer,也可以是 Durable Subscriber
  • Polling Consumer 也可以是 Transactional Client讓消費者能控制訊息何時真正從通道移除
範例:JMS receive 與 .NET Receive

JMS#

MessageConsumer 有三種 receive 方法:

  1. receive()——阻塞直到有訊息可用,然後回傳它
  2. receiveNoWait()——檢查一次,回傳訊息或 null
  3. receive(long)——阻塞直到有訊息可用並回傳,或直到逾時並回傳 null
Destination dest = // Get the destination
Session session = // Create the session
MessageConsumer consumer = session.createConsumer(dest);
Message message = consumer.receive();

.NET#

MessageQueue 客戶端有數種 Receive 變體,最簡單的兩種:

  1. Receive()——阻塞直到有訊息可用,然後回傳它
  2. Receive(TimeSpan)——阻塞直到有訊息可用並回傳,或直到逾時並拋出 MessageQueueException
MessageQueue queue = // Get the queue
Message message = queue.Receive();