Padrão de fila de prioridadePriority Queue pattern

Priorize as solicitações enviadas a serviços para que as solicitações com uma prioridade mais alta sejam recebidas e processadas mais rapidamente do que aquelas com uma prioridade mais baixa.Prioritize requests sent to services so that requests with a higher priority are received and processed more quickly than those with a lower priority. Esse padrão é útil em aplicativos que oferecem garantias de nível de serviço diferentes para clientes individuais.This pattern is useful in applications that offer different service level guarantees to individual clients.

Contexto e problemaContext and Problem

Os aplicativos podem delegar tarefas específicas para outros serviços, por exemplo, para executar processamento em segundo plano ou para se integrar com outros aplicativos ou serviços.Applications can delegate specific tasks to other services, for example, to perform background processing or to integrate with other applications or services. Na nuvem, uma fila de mensagens é normalmente usada para delegar as tarefas para processamento em segundo plano.In the cloud, a message queue is typically used to delegate tasks to background processing. Em muitos casos, as solicitações do pedido são recebidas por um serviço que não é importante.In many cases the order requests are received in by a service isn't important. No entanto, em alguns casos, é necessário priorizar solicitações específicas.In some cases, though, it's necessary to prioritize specific requests. Essas solicitações devem ser processadas antes que as solicitações de prioridade mais baixa enviadas anteriormente pelo aplicativo.These requests should be processed earlier than lower priority requests that were sent previously by the application.

SoluçãoSolution

Uma fila é geralmente uma estrutura PEPS (primeiro a entrar, primeiro a sair), e os consumidores normalmente recebem mensagens na mesma ordem em que elas foram postadas na fila.A queue is usually a first-in, first-out (FIFO) structure, and consumers typically receive messages in the same order that they were posted to the queue. No entanto, algumas filas de mensagens dão suporte a mensagens de prioridade.However, some message queues support priority messaging. O aplicativo que posta uma mensagem pode atribuir uma prioridade, e as mensagens na fila são reordenadas automaticamente para que aquelas com prioridade mais alta sejam recebidas antes daquelas com prioridade mais baixa.The application posting a message can assign a priority and the messages in the queue are automatically reordered so that those with a higher priority will be received before those with a lower priority. A figura ilustra uma fila com a prioridade das mensagens.The figure illustrates a queue with priority messaging.

Figura 1 – Uso de um mecanismo de enfileiramento que dá suporte à priorização de mensagem

A maioria das implementações de fila de mensagens dá suporte a vários consumidores (seguindo o padrão de Consumidores concorrentes), e o número de processos do consumidor pode ser escalado verticalmente ou reduzido horizontalmente dependendo da demanda.Most message queue implementations support multiple consumers (following the Competing Consumers pattern), and the number of consumer processes can be scaled up or down depending on demand.

Em sistemas que não dão suporte a filas de mensagens baseadas em prioridade, uma solução alternativa é manter uma fila separada para cada prioridade.In systems that don't support priority-based message queues, an alternative solution is to maintain a separate queue for each priority. O aplicativo é responsável por postar mensagens na fila apropriada.The application is responsible for posting messages to the appropriate queue. Cada fila pode ter um pool separado de consumidores.Each queue can have a separate pool of consumers. Filas de prioridade mais alta podem ter um pool maior de consumidores em execução no hardware mais rápido que as filas de prioridade mais baixa.Higher priority queues can have a larger pool of consumers running on faster hardware than lower priority queues. A figura a seguir ilustra o uso de filas de mensagens separadas para cada prioridade.The next figure illustrates using separate message queues for each priority.

Figura 2 – Uso de filas de mensagens separadas para cada prioridade

Uma variação dessa estratégia é ter um único pool de consumidores que verifica se há mensagens em filas de alta prioridade primeiro e só então começa a buscar mensagens das filas de prioridade mais baixa.A variation on this strategy is to have a single pool of consumers that check for messages on high priority queues first, and only then start to fetch messages from lower priority queues. Há algumas diferenças semânticas entre uma solução que usa um único pool de processos do consumidor (com uma única fila que dá suporte a mensagens com prioridades diferentes ou com várias filas que cada uma lida com mensagens de uma única prioridade) e uma solução que usa várias filas com um pool separado para cada fila.There are some semantic differences between a solution that uses a single pool of consumer processes (either with a single queue that supports messages with different priorities or with multiple queues that each handle messages of a single priority), and a solution that uses multiple queues with a separate pool for each queue.

Na abordagem de pool único, as mensagens de prioridade mais alta sempre são recebidas e processadas antes das mensagens de prioridade mais baixa.In the single pool approach, higher priority messages are always received and processed before lower priority messages. Em teoria, mensagens com uma prioridade muito baixa poderiam ser substituídas continuamente e, talvez, nunca seriam processadas.In theory, messages that have a very low priority could be continually superseded and might never be processed. Na abordagem de vários pools, as mensagens de prioridade mais baixa serão sempre processadas; não tão rapidamente quanto aquelas de uma prioridade mais alta (dependendo do tamanho relativo dos pools e dos recursos que elas têm disponíveis).In the multiple pool approach, lower priority messages will always be processed, just not as quickly as those of a higher priority (depending on the relative size of the pools and the resources that they have available).

Usar um mecanismo de enfileiramento de prioridade pode fornecer as seguintes vantagens:Using a priority queuing mechanism can provide the following advantages:

  • Ele permite que os aplicativos atendam aos requisitos de negócios que requerem a priorização de disponibilidade ou desempenho, como oferecer níveis diferentes de serviço para grupos de clientes específicos.It allows applications to meet business requirements that require prioritization of availability or performance, such as offering different levels of service to specific groups of customers.

  • Ele pode ajudar a minimizar os custos operacionais.It can help to minimize operational costs. Na abordagem de fila única, você pode reduzir o número de consumidores se necessário.In the single queue approach, you can scale back the number of consumers if necessary. Mensagens de alta prioridade ainda serão processadas primeiro (embora possivelmente de modo mais lento), e mensagens de prioridade mais baixa podem ser atrasadas por mais tempo.High priority messages will still be processed first (although possibly more slowly), and lower priority messages might be delayed for longer. Se você já implementou a abordagem de fila de mensagens com vários pools separados de consumidores para cada fila, pode reduzir o pool de consumidores de filas de prioridade mais baixa ou mesmo suspender o processamento para algumas filas de prioridade muito baixa, interrompendo todos os consumidores que escutam mensagens nessas filas.If you've implemented the multiple message queue approach with separate pools of consumers for each queue, you can reduce the pool of consumers for lower priority queues, or even suspend processing for some very low priority queues by stopping all the consumers that listen for messages on those queues.

  • A abordagem de várias filas de mensagens pode ajudar a maximizar o desempenho e a escalabilidade do aplicativo por meio do particionamento das mensagens com base nos requisitos de processamento.The multiple message queue approach can help maximize application performance and scalability by partitioning messages based on processing requirements. Por exemplo, tarefas vitais podem ser priorizadas para serem manipuladas pelo receptores que executam imediatamente enquanto tarefas em segundo plano menos importantes podem ser tratadas por receptores que são agendados para executar em períodos menos ocupados.For example, vital tasks can be prioritized to be handled by receivers that run immediately while less important background tasks can be handled by receivers that are scheduled to run at less busy periods.

Problemas e consideraçõesIssues and Considerations

Considere os seguintes pontos ao decidir como implementar esse padrão:Consider the following points when deciding how to implement this pattern:

Defina as prioridades no contexto da solução.Define the priorities in the context of the solution. Por exemplo, alta prioridade pode significar que as mensagens devem ser processadas dentro de dez segundos.For example, high priority could mean that messages should be processed within ten seconds. Identifique os requisitos para lidar com itens de alta prioridade e os outros recursos que devem ser alocados para atender a esses critérios.Identify the requirements for handling high priority items, and the other resources that should be allocated to meet these criteria.

Decida se todos os itens de alta prioridade devem ser processados antes dos itens de prioridade mais baixa.Decide if all high priority items must be processed before any lower priority items. Se as mensagens estão sendo processadas por um único pool de consumidores, você precisa fornecer um mecanismo que pode impedir e suspender uma tarefa que lida com uma mensagem de baixa prioridade se uma mensagem de prioridade mais alta for disponibilizada.If the messages are being processed by a single pool of consumers, you have to provide a mechanism that can preempt and suspend a task that's handling a low priority message if a higher priority message becomes available.

Na abordagem de várias filas, ao usar um único pool de processos do consumidor que escuta todas as filas em vez de um pool de consumidores exclusivo para cada fila, o consumidor deve aplicar um algoritmo que garante que ele sempre atenda as mensagens das filas de prioridade mais alta antes das filas de prioridade mais baixa.In the multiple queue approach, when using a single pool of consumer processes that listen on all queues rather than a dedicated consumer pool for each queue, the consumer must apply an algorithm that ensures it always services messages from higher priority queues before those from lower priority queues.

Monitore a velocidade do processamento nas filas de prioridade alta e baixa para garantir que as mensagens nessas filas sejam processadas a taxas esperadas.Monitor the processing speed on high and low priority queues to ensure that messages in these queues are processed at the expected rates.

Se você precisa garantir que as mensagens de baixa prioridade serão processadas, é necessário implementar a abordagem de várias filas de mensagens com vários pools de consumidores.If you need to guarantee that low priority messages will be processed, it's necessary to implement the multiple message queue approach with multiple pools of consumers. Como alternativa, em uma fila que dá suporte à priorização de mensagem, é possível aumentar dinamicamente a prioridade de uma mensagem na fila à medida que ela fica antiga.Alternatively, in a queue that supports message prioritization, it's possible to dynamically increase the priority of a queued message as it ages. No entanto, essa abordagem depende da fila de mensagens fornecer esse recurso.However, this approach depends on the message queue providing this feature.

Usar uma fila separada para cada prioridade de mensagem funciona melhor para sistemas que têm um número pequeno de prioridades bem definidas.Using a separate queue for each message priority works best for systems that have a small number of well-defined priorities.

As prioridades da mensagem podem ser determinadas logicamente pelo sistema.Message priorities can be determined logically by the system. Por exemplo, em vez de ter mensagens de prioridade alta e baixa explícitas, elas podem ser designadas como "cliente pagante" ou "cliente não pagante".For example, rather than having explicit high and low priority messages, they could be designated as “fee paying customer,” or “non-fee paying customer.” Dependendo de seu modelo de negócios, seu sistema pode alocar mais recursos para processamento de mensagens de clientes pagantes que dos clientes não pagantes.Depending on your business model, your system can allocate more resources to processing messages from fee paying customers than non-fee paying ones.

Pode haver um custo financeiro e de processamento associado à verificação de mensagens em uma fila (alguns sistemas de mensagens comerciais cobram uma pequena taxa sempre que uma mensagem é postada ou recuperada e sempre que uma fila é consultada quanto a mensagens).There might be a financial and processing cost associated with checking a queue for a message (some commercial messaging systems charge a small fee each time a message is posted or retrieved, and each time a queue is queried for messages). Esse custo aumenta ao verificar várias filas.This cost increases when checking multiple queues.

É possível ajustar dinamicamente o tamanho de um pool de consumidores com base no comprimento da fila que o pool está atendendo.It's possible to dynamically adjust the size of a pool of consumers based on the length of the queue that the pool is servicing. Para saber mais, confira as Diretrizes de dimensionamento automático.For more information, see the Autoscaling Guidance.

Quando usar esse padrãoWhen to use this pattern

Esse padrão é útil em cenários em que:This pattern is useful in scenarios where:

  • O sistema deve lidar com várias tarefas que têm prioridades diferentes.The system must handle multiple tasks that have different priorities.

  • Diferentes usuários ou locatários devem ser atendidos com prioridades diferentes.Different users or tenants should be served with different priority.

ExemploExample

O Microsoft Azure não fornece um mecanismo de enfileiramento que dê suporte nativo à priorização automática de mensagens por meio de classificação.Microsoft Azure doesn't provide a queuing mechanism that natively supports automatic prioritization of messages through sorting. No entanto, ele fornece tópicos e assinaturas do Barramento de Serviço do Azure que dão suporte a um mecanismo de enfileiramento que fornece filtragem de mensagens, junto com uma ampla variedade de recursos flexíveis que o tornam ideal para ser usado na maioria das implementações de fila de prioridade.However, it does provide Azure Service Bus topics and subscriptions that support a queuing mechanism that provides message filtering, together with a wide range of flexible capabilities that make it ideal for use in most priority queue implementations.

Uma solução do Azure pode implementar um tópico de Barramento de Serviço e um aplicativo pode postar mensagens, da mesma forma que uma fila.An Azure solution can implement a Service Bus topic an application can post messages to, in the same way as a queue. As mensagens podem conter metadados na forma de propriedades personalizadas definidas pelo aplicativo.Messages can contain metadata in the form of application-defined custom properties. As assinaturas do Barramento de Serviço podem ser associadas ao tópico, e estas assinaturas podem filtrar mensagens com base em suas propriedades.Service Bus subscriptions can be associated with the topic, and these subscriptions can filter messages based on their properties. Quando um aplicativo envia uma mensagem para um tópico, a mensagem é direcionada para a assinatura apropriada onde ela pode ser lida por um consumidor.When an application sends a message to a topic, the message is directed to the appropriate subscription where it can be read by a consumer. Os processos do consumidor podem recuperar mensagens de uma assinatura usando a mesma semântica como uma fila de mensagens (uma assinatura é uma fila lógica).Consumer processes can retrieve messages from a subscription using the same semantics as a message queue (a subscription is a logical queue). A figura a seguir ilustra a implementação de uma fila de prioridade com tópicos e assinaturas do Barramento de Serviço do Azure.The following figure illustrates implementing a priority queue with Azure Service Bus topics and subscriptions.

Figura 3 – Implementação de uma fila de prioridade com tópicos e assinaturas do Barramento de Serviço do Azure

Na figura acima, o aplicativo cria várias mensagens e atribui uma propriedade personalizada chamada Priority em cada mensagem com um valor, High ou Low.In the figure above, the application creates several messages and assigns a custom property called Priority in each message with a value, either High or Low. O aplicativo posta essas mensagens em um tópico.The application posts these messages to a topic. O tópico tem duas assinaturas associadas que filtram mensagens examinando a propriedade Priority.The topic has two associated subscriptions that both filter messages by examining the Priority property. Uma assinatura aceita mensagens onde a propriedade Priority está definida como High, e o outra aceita mensagens onde a propriedade Priority está definida como Low.One subscription accepts messages where the Priority property is set to High, and the other accepts messages where the Priority property is set to Low. Um pool de consumidores lê as mensagens de cada assinatura.A pool of consumers reads messages from each subscription. A assinatura de alta prioridade tem um pool maior, e esses consumidores podem estar em execução em computadores mais potentes com mais recursos disponíveis que os consumidores no pool de baixa prioridade.The high priority subscription has a larger pool, and these consumers might be running on more powerful computers with more resources available than the consumers in the low priority pool.

Observe que não há nada de especial sobre a designação de mensagens de prioridade alta e baixa neste exemplo.Note that there's nothing special about the designation of high and low priority messages in this example. Elas são simplesmente rótulos especificados como propriedades em cada mensagem e são usadas para direcionar mensagens para uma assinatura específica.They're simply labels specified as properties in each message, and are used to direct messages to a specific subscription. Se forem necessárias prioridades adicionais, será relativamente fácil criar mais assinaturas e pools de processos do consumidor para lidar com essas prioridades.If additional priorities are required, it's relatively easy to create further subscriptions and pools of consumer processes to handle these priorities.

A solução PriorityQueue disponível em GitHub contém uma implementação dessa abordagem.The PriorityQueue solution available on GitHub contains an implementation of this approach. Essa solução contém dois projetos de função de trabalho chamados PriorityQueue.High e PriorityQueue.Low.This solution contains two worker role projects named PriorityQueue.High and PriorityQueue.Low. Essas funções de trabalho herdam a classe PriorityWorkerRole que contém a funcionalidade para se conectar a uma assinatura especificada no método OnStart.These worker roles inherit from the PriorityWorkerRole class that contains the functionality for connecting to a specified subscription in the OnStart method.

As funções de trabalho PriorityQueue.High e PriorityQueue.Low se conectam a assinaturas diferentes, definidas por suas definições de configuração.The PriorityQueue.High and PriorityQueue.Low worker roles connect to different subscriptions, defined by their configuration settings. Um administrador pode configurar diferentes números de cada função a ser executada.An administrator can configure different numbers of each role to be run. Normalmente haverá mais instâncias da função de trabalho PriorityQueue.High que da função de trabalho PriorityQueue.Low.Typically there'll be more instances of the PriorityQueue.High worker role than the PriorityQueue.Low worker role.

O método Run na classe PriorityWorkerRole organiza o método ProcessMessage virtual (também definido na classe PriorityWorkerRole) a ser executado para cada mensagem recebida na fila.The Run method in the PriorityWorkerRole class arranges for the virtual ProcessMessage method (also defined in the PriorityWorkerRole class) to be run for each message received on the queue. O código a seguir mostra os métodos Run e ProcessMessage.The following code shows the Run and ProcessMessage methods. A classe QueueManager, definida no projeto PriorityQueue.Shared, fornece métodos auxiliares para usar filas do Barramento de Serviço do Azure.The QueueManager class, defined in the PriorityQueue.Shared project, provides helper methods for using Azure Service Bus queues.

public class PriorityWorkerRole : RoleEntryPoint
{
  private QueueManager queueManager;
  ...

  public override void Run()
  {
    // Start listening for messages on the subscription.
    var subscriptionName = CloudConfigurationManager.GetSetting("SubscriptionName");
    this.queueManager.ReceiveMessages(subscriptionName, this.ProcessMessage);
    ...;
  }
  ...

  protected virtual async Task ProcessMessage(BrokeredMessage message)
  {
    // Simulating processing.
    await Task.Delay(TimeSpan.FromSeconds(2));
  }
}

As funções de trabalho PriorityQueue.High e PriorityQueue.Low substituem a funcionalidade padrão do método ProcessMessage.The PriorityQueue.High and PriorityQueue.Low worker roles both override the default functionality of the ProcessMessage method. O código a seguir mostra o método ProcessMessage para a função de trabalho PriorityQueue.High.The code below shows the ProcessMessage method for the PriorityQueue.High worker role.

protected override async Task ProcessMessage(BrokeredMessage message)
{
  // Simulate message processing for High priority messages.
  await base.ProcessMessage(message);
  Trace.TraceInformation("High priority message processed by " +
    RoleEnvironment.CurrentRoleInstance.Id + " MessageId: " + message.MessageId);
}

Quando um aplicativo posta mensagens no tópico associado às assinaturas usadas pelas funções de trabalho PriorityQueue.High e PriorityQueue.Low, ele especifica a prioridade usando a propriedade Priority personalizada, conforme mostrado no exemplo de código a seguir.When an application posts messages to the topic associated with the subscriptions used by the PriorityQueue.High and PriorityQueue.Low worker roles, it specifies the priority by using the Priority custom property, as shown in the following code example. Esse código (implementado na classe WorkerRole no projeto PriorityQueue.Sender), usa o método auxiliar SendBatchAsync da classe QueueManager para postar mensagens em um tópico em lotes.This code (implemented in the WorkerRole class in the PriorityQueue.Sender project), uses the SendBatchAsync helper method of the QueueManager class to post messages to a topic in batches.

// Send a low priority batch.
var lowMessages = new List<BrokeredMessage>();

for (int i = 0; i < 10; i++)
{
  var message = new BrokeredMessage() { MessageId = Guid.NewGuid().ToString() };
  message.Properties["Priority"] = Priority.Low;
  lowMessages.Add(message);
}

this.queueManager.SendBatchAsync(lowMessages).Wait();
...

// Send a high priority batch.
var highMessages = new List<BrokeredMessage>();

for (int i = 0; i < 10; i++)
{
  var message = new BrokeredMessage() { MessageId = Guid.NewGuid().ToString() };
  message.Properties["Priority"] = Priority.High;
  highMessages.Add(message);
}

this.queueManager.SendBatchAsync(highMessages).Wait();

Os padrões e diretrizes a seguir também podem ser relevantes ao implementar esse padrão:The following patterns and guidance might also be relevant when implementing this pattern:

  • Um exemplo que demonstra esse padrão está disponível em GitHub.A sample that demonstrates this pattern is available on GitHub.

  • Prévia de mensagens assíncronas.Asynchronous Messaging Primer. Um serviço do consumidor que processa uma solicitação talvez precise enviar uma resposta para a instância do aplicativo que postou a solicitação.A consumer service that processes a request might need to send a reply to the instance of the application that posted the request. Fornece informações sobre as estratégias que você pode usar para implementar mensagens de solicitação/resposta.Provides information on the strategies that you can use to implement request/response messaging.

  • Padrão consumidores concorrentes.Competing Consumers pattern. Para aumentar a taxa de transferência das filas, é possível ter vários consumidores que escutam na mesma fila e processar as tarefas em paralelo.To increase the throughput of the queues, it’s possible to have multiple consumers that listen on the same queue, and process the tasks in parallel. Esses consumidores competirão por mensagens, mas apenas um deve ser capaz de processar cada mensagem.These consumers will compete for messages, but only one should be able to process each message. Fornece mais informações sobre as vantagens e desvantagens de implementar essa abordagem.Provides more information on the benefits and tradeoffs of implementing this approach.

  • Padrão de estrangulamento.Throttling pattern. Você pode implementar a limitação usando filas.You can implement throttling by using queues. A prioridade de mensagens pode ser usada para garantir que as solicitações de aplicativos críticos ou aplicativos sendo executados por clientes de alto valor recebam prioridade sobre solicitações de aplicativos menos importantes.Priority messaging can be used to ensure that requests from critical applications, or applications being run by high-value customers, are given priority over requests from less important applications.

  • Orientação de autodimensionamento.Autoscaling Guidance. É possível dimensionar o tamanho do pool de processos do consumidor manipulando uma fila dependendo do tamanho dela.It might be possible to scale the size of the pool of consumer processes handling a queue depending on the length of the queue. Essa estratégia pode ajudar a melhorar o desempenho, especialmente para pools que lidam com mensagens de alta prioridade.This strategy can help to improve performance, especially for pools handling high priority messages.

  • Padrões de integração empresarial com o Barramento de Serviço no blog de Abhishek Lal.Enterprise Integration Patterns with Service Bus on Abhishek Lal’s blog.