Competing Consumers Pattern (競爭消費者模式)

weixin_34162629發表於2016-02-23

Enable multiple concurrent consumers to process messages received on the same messaging channel. This pattern enables a system to process multiple messages concurrently to optimize throughput, to improve scalability and availability, and to balance the workload.

使多個併發的消費者來處理同一訊息收發通道接收到的訊息。這種模式使系統能夠同時處理多個訊息以優化吞吐量,提高擴充套件性和可用性,並且以平衡的工作量。

Context and Problem 背景與問題

An application running in the cloud may be expected to handle a large number of requests. Rather than process each request synchronously, a common technique is for the application to pass them through a messaging system to another service (a consumer service) that handles them asynchronously. This strategy helps to ensure that the business logic in the application is not blocked while the requests are being processed.

在雲中執行的應用程式可以預期處理大量的請求。而不是過程中的每個請求的同步,常用的技術是應用程式通過訊息系統來處理這些非同步其它服務(消費者服務)來傳遞他們。這種策略有助於確保當請求正在處理中的應用程式的業務邏輯沒有被阻塞。

The number of requests could vary significantly over time for many reasons. A sudden burst in user activity or aggregated requests coming from multiple tenants may cause unpredictable workload. At peak hours a system might need to process many hundreds of requests per second, while at other times the number could be very small. Additionally, the nature of the work performed to handle these requests might be highly variable. Using a single instance of the consumer service might cause that instance to become flooded with requests or the messaging system may be overloaded by an influx of messages coming from the application. To handle this fluctuating workload, the system can run multiple instances of the consumer service. However these consumers must be coordinated to ensure that each message is only delivered to a single consumer. The workload also needs to be load balanced across consumers to prevent an instance from becoming a bottleneck.

請求的數目可能會隨著時間的推移等原因變化比較顯著。在使用者活動或自多個租戶的請求突然爆發可能會導致不可預知的工作負荷。在高峰時間系統可能需要每秒處理數以百計的請求,而在其他時間的可能是非常小的。此外,工作性質執行以處理這些請求可能是高度可變的。利用單一消費者服務可能導致該例項充斥著請求或訊息系統過載當從一個湧入而來的訊息應用。為了處理這種波動的工作負荷,系統可以執行多個消費者服務例項。然而,這些消費者必須協調,以確保每個訊息只傳送到單一的消費者。工作負荷也需要負載跨越消費者平衡,以防止一個例項成為瓶頸。

Solution 解決方案

Use a message queue to implement the communication channel between the application and the instances of the consumer service. The application posts requests in the form of messages to the queue, and the consumer service instances receive messages from the queue and process them. This approach enables the same pool of consumer service instances to handle messages from any instance of the application. Figure 1 illustrates this architecture.

使用訊息佇列來實現應用和消費者服務例項之間的通訊通道。應用程式提交標段的訊息請求到訊息佇列,以及消費者服務例項從佇列接收訊息並對其進行處理。這種方法使同一池的消費者服務例項處理任何應用程式例項的訊息。圖1示出了該架構。

Figure 1 - Using a message queue to distribute work to instances of a service

This solution offers the following benefits:該解決方案具有以下優點:

  • It enables an inherently load-leveled system that can handle wide variations in the volume of requests sent by application instances. The queue acts as a buffer between the application instances and the consumer service instances, which can help to minimize the impact on availability and responsiveness for both the application and the service instances (as described by the Queue-based Load Leveling pattern). Handling a message that requires some long-running processing to be performed does not prevent other messages from being handled concurrently by other instances of the consumer service.
  • 它是一種固有的負荷調平系統,可以處理應用程式傳送批量請求的變化比較大的情況。佇列在應用程式例項和客戶服務的之間充當一個緩衝,它可以最大限度減少應用程式和服務例項在可用性和響應的影響。處理一個訊息需要長時間執行只到被處理完畢但不會阻止消費者服務例項的其他訊息併發。
  • It improves reliability. If a producer communicates directly with a consumer instead of using this pattern, but does not monitor the consumer, there is a high probability that messages could be lost or fail to be processed if the consumer fails. In this pattern messages are not sent to a specific service instance, a failed service instance will not block a producer, and messages can be processed by any working service instance.
  • 它提高了可靠性。當然生產者與消費者直接通訊而取代使用這種模式,但不監視消費者,訊息會可能丟失或失敗的可能性比較高,如果消費者無法進行處理。在這種模式訊息不傳送到一個特定的服務例項,失敗的服務例項將不會阻止一個生產者,且訊息可以通過任何工作服務例項進行處理。
  • It does not require complex coordination between the consumers, or between the producer and the consumer instances. The message queue ensures that each message is delivered at least once.
  • 它不需要在消費者之間,或生產者和消費者的例項之間進行復雜的協調。訊息佇列確保每個訊息被傳遞至少一次。
  • It is scalable. The system can dynamically increase or decrease the number of instances of the consumer service as the volume of messages fluctuates.
  • 它是可伸縮的。該系統可以動態增加或減少消費者服務的例項數目滿足訊息的體積波動。
  • It can improve resiliency if the message queue provides transactional read operations. If a consumer service instance reads and processes the message as part of a transactional operation, and if this consumer service instance subsequently fails, this pattern can ensure that the message will be returned to the queue to be picked up and handled by another instance of the consumer service.
  • 它可以提高應變能力如果訊息佇列的提供事務讀取操作時。如果消費者服務例項讀取和處理該訊息作為事務操作的一部分,並且如果這種消費服務例項隨後發生故障時,該模式可以確保訊息將被返回到佇列並被另一個消費者服務例項拾起和處理。

Issues and Considerations 問題和注意事項

Consider the following points when deciding how to implement this pattern:

在決定如何實施這一模式時,請考慮以下幾點:

Message Ordering. The order in which consumer service instances receive messages is not guaranteed, and does not necessarily reflect the order in which the messages were created. Design the system to ensure that message processing is idempotent because this will help to eliminate any dependency on the order in which messages are handled. For more information about idempotency, see Idempotency Patterns on Jonathon Oliver’s blog.

•訊息訂購。在消費者服務例項接收訊息的順序不能保證,並不一定反映在其中建立的訊息的順序。設計系統,確保資訊處理等冪的,因為這將有助於消除在訊息的處理順序上的任何依賴。有關冪等的詳細資訊,請參閱喬納森·奧利弗的部落格冪等模式。

Note:

Microsoft Azure Service Bus Queues can implement guaranteed first-in-first-out ordering of messages by using message sessions. For more information, see Messaging Patterns Using Sessions on MSDN.

Designing Services for Resiliency. If the system is designed to detect and restart failed service instances, it may be necessary to implement the processing performed by the service instances as idempotent operations to minimize the effects of a single message being retrieved and processed more than once.

•設計服務彈性。如系統被設計為檢測和重新啟動當服務初始化失敗,可能有必要實施處理並由服務例項作為冪運算執行,以儘量減少檢索和處理一次以上的單個訊息的影響的處理。

Detecting Poison Messages. A malformed message, or a task that requires access to resources that are not available, may cause a service instance to fail. The system should prevent such messages being returned to the queue, and instead capture and store the details of these messages elsewhere so that they can be analyzed if necessary.

•檢測有害訊息。格式不正確的訊息或需要訪問不可用的資源的任務,可能會造成服務例項失敗。該系統應防止這樣的訊息被返回到佇列,而是捕獲和在別處儲存這些訊息的細節,以便它們可以在需要進行分析。

Handling Results. The service instance handling a message is fully decoupled from the application logic that generates the message, and they may not be able to communicate directly. If the service instance generates results that must be passed back to the application logic, this information must be stored in a location that is accessible to both and the system must provide some indication of when processing has completed to prevent the application logic from retrieving incomplete data.

•處理結果。服務例項處理的訊息是從生成該訊息的應用程式邏輯完全解耦,並且它們未必能夠直接進行通訊。如果服務例項生成的結果必須傳遞迴應用程式邏輯,此資訊必須被儲存在是兩者可訪問的位置,並且系統必須提供當處理已經完成,以防止從檢索不完全資料的應用程式邏輯的一些指示。

Note:

If you are using Azure, a worker process may be able to pass results back to the application logic by using a dedicated message reply queue. The application logic must be able to correlate these results with the original message. This scenario is described in more detail in the Asynchronous Messaging Primer.

Scaling the Messaging System. In a large-scale solution, a single message queue could be overwhelmed by the number of messages and become a bottleneck in the system. In this situation, consider partitioning the messaging system to direct messages from specific producers to a particular queue, or use load balancing to distribute messages across multiple message queues.
•伸縮的訊息系統。在一個大型可伸縮性的解決方案中,一個訊息佇列由訊息的數量未知而不知所措,成為在系統中的瓶頸。在這種情況下,考慮分割該訊息系統以引導從特定生產者的資訊到一個特定的佇列,或使用負載平衡通過多個訊息佇列分發訊息。

Ensuring Reliability of the Messaging System. A reliable messaging system is needed to guarantee that, once the application enqueues a message, it will not be lost. This is essential for ensuring that all messages are delivered at least once.
•訊息系統的可靠性保障。需要一個可靠的訊息傳遞系統來保證,一旦應用程式排隊的訊息,它也不會丟失。這是確保所有訊息都至少有一次交付至關重要。

When to Use this Pattern 什麼時候用這個模式

Use this pattern when:使用此模式時:

  • The workload for an application is divided into tasks that can run asynchronously.
  • 一個應用程式的工作負荷被分成可以非同步執行任務。
  • Tasks are independent and can run in parallel.
  • 任務是獨立的,並且可以並行地執行。
  • The volume of work is highly variable, requiring a scalable solution.
  • 工作容積是高度可變的,因此需要一種可擴充套件的解決方案。
  • The solution must provide high availability, and must be resilient if the processing for a task fails.
  • 該解決方案必須提供高可用性,並且如果一個任務的處理失敗,必須是有彈性的。

This pattern may not be suitable when:這種模式可能不適合時:

  • It is not easy to separate the application workload into discrete tasks, or there is a high degree of dependence between tasks.
  • 對應用程式的工作負荷分割成離散的任務是不容易的,或者任務之間有高度的依賴。
  • Tasks must be performed synchronously, and the application logic must wait for a task to complete before continuing.
  • 任務必須同步進行,並且應用程式邏輯必須等待任務完成後再繼續。
  • Tasks must be performed in a specific sequence.
  • 任務必須以特定的順序來執行。

Note:注意:

Some messaging systems support sessions that enable a producer to group messages together and ensure that they are all handled by the same consumer. This mechanism can be used with prioritized messages (if they are supported) to implement a form of message ordering that delivers messages in sequence from a producer to a single consumer.

一些訊息系統支援會話,使生產者對訊息進行分組在一起,並確保它們都被同一個接收者處理。該機制可以與優先訊息中使用(如果它們支援)來實現訊息排序的形式,從生產者傳送訊息中的序列到單個消費者。

Example 例子

Azure provides storage queues and Service Bus queues that can act as a suitable mechanism for implementing this pattern. The application logic can post messages to a queue, and consumers implemented as tasks in one or more roles can retrieve messages from this queue and process them. For resiliency, a Service Bus queue enables a consumer to use PeekLock mode when it retrieves a message from the queue. This mode does not actually remove the message, but simply hides it from other consumers. The original consumer can delete the message when it has finished processing it. If the consumer should fail, the peek lock will time out and the message will become visible again, allowing another consumer to retrieve it.

Azure提供儲存佇列和服務匯流排佇列,可以作為適當的機制實施此模式。應用邏輯可以釋出訊息到佇列,而消費者實現為在一個或多個角色的任務可以從這個佇列中檢索訊息並進行處理。對於彈性,服務匯流排佇列使消費者使用PeekLock模式時,它就從佇列中的訊息。這種模式實際上並沒有刪除該訊息,而只是隱藏它從其他消費者。當處理完它原來的消費者可以刪除郵件。如果消費者失敗,PeekLock將超時,訊息將再次變得可見,讓另一個消費者進行取回。

Note:

For detailed information on using Azure Service Bus queues, see Service Bus Queues, Topics, and Subscriptions on MSDN. For information on using Azure storage queues, see How to use the Queue Storage Service on MSDN.

The following code shows from the QueueManager class in CompetingConsumers solution of the examples available for download for this guidance shows how you can create a queue by using a QueueClient instance in the Start event handler in a web or worker role.

下面的程式碼從競爭的可供下載的例子中的CompetingConsumers解決方案QueueManager類展示了這個指導說明了如何可以通過在網路或輔助角色Start的事件處理程式使用QueueClient例項建立佇列。

private string queueName = ...;
private string connectionString = ...;
...

public async Task Start()
{
  // Check if the queue already exists.
  var manager = NamespaceManager.CreateFromConnectionString(this.connectionString);
  if (!manager.QueueExists(this.queueName))
  {
    var queueDescription = new QueueDescription(this.queueName);

    // Set the maximum delivery count for messages in the queue. A message 
    // is automatically dead-lettered after this number of deliveries. The
    // default value for dead letter count is 10.
    queueDescription.MaxDeliveryCount = 3;

    await manager.CreateQueueAsync(queueDescription);
  }
  ...

  // Create the queue client. By default the PeekLock method is used.
  this.client = QueueClient.CreateFromConnectionString(
    this.connectionString, this.queueName);
}

The next code snippet shows how an application can create and send a batch of messages to the queue.

下面的程式碼片段展示了一個應用程式如何建立和傳送一批訊息佇列。

public async Task SendMessagesAsync()
{
  // Simulate sending a batch of messages to the queue.
  var messages = new List<BrokeredMessage>();

  for (int i = 0; i < 10; i++)
  {
    var message = new BrokeredMessage() { MessageId = Guid.NewGuid().ToString() };
    messages.Add(message);
  }
  await this.client.SendBatchAsync(messages);
}

The following code shows how a consumer service instance can receive messages from the queue by following an event-driven approach. The processMessageTask parameter to the ReceiveMessages method is a delegate that references the code to run when a message is received. This code is run asynchronously.

下面的程式碼演示瞭如何消費服務例項可以從佇列遵循事件驅動方式接收訊息。該processMessageTask引數來ReceiveMessages方法是引用程式碼收到訊息時執行的委託。此程式碼非同步執行。

private ManualResetEvent pauseProcessingEvent;
...

public void ReceiveMessages(Func<BrokeredMessage, Task> processMessageTask)
{
  // Set up the options for the message pump.
  var options = new OnMessageOptions();

  // When AutoComplete is disabled it is necessary to manually
  // complete or abandon the messages and handle any errors.
  options.AutoComplete = false;
  options.MaxConcurrentCalls = 10;
  options.ExceptionReceived += this.OptionsOnExceptionReceived;

  // Use of the Service Bus OnMessage message pump. 
  // The OnMessage method must be called once, otherwise an exception will occur.
  this.client.OnMessageAsync(
    async (msg) =>
    {
      // Will block the current thread if Stop is called.
      this.pauseProcessingEvent.WaitOne();

      // Execute processing task here.
      await processMessageTask(msg);
    },
    options);
}
...

private void OptionsOnExceptionReceived(object sender, 
  ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
  ...
}

Note that autoscaling features, such as those available in Azure, can be used to start and stop role instances as the queue length fluctuates. For more information, see Autoscaling Guidance. In addition, it is not necessary to maintain a one-to-one correspondence between role instances and worker processes—a single role instance can implement multiple worker processes. For more information, see Compute Resource Consolidation pattern.

注意,自動縮放的功能,如在Azure中那些可用,可作為佇列長度波動來啟動和停止角色例項。欲瞭解更多資訊,請參閱自動縮放指導。此外,這是沒有必要維持角色例項和工人之間的一對一的對應關係的過程,一個單一的角色例項可以實現多個工作程式。欲瞭解更多資訊,請參閱計算資源整合模式。

Related Patterns and Guidance 相關模式和指導

The following patterns and guidance may be relevant when implementing this pattern:

實施這一模式時,以下模式和指導可能是相關的:

  • Asynchronous Messaging Primer. Message queues are an inherently asynchronous communications mechanism. If a consumer service needs to send a reply to an application, it may be necessary to implement some form of response messaging. The Asynchronous Messaging Primer provides information on how to implement request/reply messaging by using message queues.
  • 非同步訊息傳遞機制。訊息佇列是一種固有的非同步通訊機制。如果消費者服務需要一個答覆傳送給一個應用程式,可能有必要實現某種形式的響應訊息的。非同步訊息傳遞機制提供有關如何實施請求/使用訊息佇列的回覆訊息的資訊。
  • Autoscaling Guidance. It may be possible to start and stop instances of a consumer service as the length of the queue to which applications post messages varies. Autoscaling can help to maintain throughput during times of peak processing.
  • 自動縮放指引。它可能會啟動和停止一消費者服務作為佇列長度的例項的應用程式釋出訊息而變化。自動配置功能可以幫助在高峰處理的時間,以保持吞吐量。
  • Compute Resource Consolidation Pattern. It may be possible to consolidate multiple instances of a consumer service into a single process to reduce costs and management overhead. The Compute Resource Consolidation pattern describes the benefits and tradeoffs of following this approach.
  • 計算資源整合模式。它可能對消費者的服務的多個例項合併成一個單一的過程,以降低成本和管理開銷。計算資源整合模式描述的好處,下面這種方法的權衡。
  • Queue-based Load Leveling Pattern. Introducing a message queue can add resiliency to the system, enabling service instances to handle widely varying volumes of requests from application instances. The message queue effectively acts as a buffer which levels the load. The Queue-based Load Leveling pattern describes this scenario in more detail.
  • 計算資源整合模式。它可能對消費者的服務的多個例項合併成一個單一的過程,以降低成本和管理開銷。計算資源整合模式描述的好處,下面這種方法的權衡。

More Information更多資訊

This pattern has a sample application associated with it. You can download the "Cloud Design Patterns – Sample Code" from the Microsoft Download Center at http://aka.ms/cloud-design-patterns-sample.

相關文章