Padrão de comboio sequencial

Agrupe mensagens relacionadas por uma chave de categoria e processe cada grupo sequencialmente, uma mensagem por vez, enquanto processa diferentes grupos em paralelo.

Este padrão resolve a tensão entre manter a correção FIFO (first-in, first-out) dentro de cada grupo lógico e escalar horizontalmente o processamento concorrente entre os grupos. O design garante que as restrições de ordenação não se tornem um gargalo em todo o sistema.

Contexto e problema

Os aplicativos geralmente precisam processar mensagens relacionadas na ordem em que chegam, sem deixar de escalar horizontalmente para lidar com o aumento da carga. Em uma arquitetura distribuída, esse requisito é difícil de atender, pois os trabalhadores buscam mensagens de forma independente em uma fila compartilhada. Quando vários trabalhadores competem por mensagens, como no Padrão de consumidores concorrentes, a ordenação é perdida.

Considere um sistema de acompanhamento de pedidos que recebe um fluxo de operações, como criar um pedido, adicionar uma transação, modificar uma transação passada e excluir um pedido. As operações de cada pedido devem ser processadas em ordem FIFO, pois aplicá-las fora de ordem corromperia o estado do pedido. No entanto, a fila de entrada intercala operações de diversos pedidos. Um único consumidor que impõe uma ordenação global torna-se um gargalo, e vários consumidores podem processar as operações de uma mesma ordem fora de sequência.

As abordagens simples para esse problema se dividem de uma maneira diferente:

  • Consumidor único. Um único consumidor preserva a ordem das mensagens porque processa uma mensagem de cada vez, mas não consegue escalar para lidar com um aumento na taxa de transferência.

  • Vários consumidores concorrentes. Vários consumidores aumentam a taxa de transferência ao buscar mensagens em paralelo, mas perdem as garantias de ordenação por grupo. Dois trabalhadores podem recuperar mensagens consecutivas referentes ao mesmo pedido e processá-las simultaneamente ou fora de sequência, o que corrompe o estado do pedido.

Solução

O padrão de comboio sequencial particiona mensagens relacionadas em categorias e processa cada categoria sequencialmente, uma mensagem por vez, enquanto as categorias são processadas em paralelo.

O padrão funciona atribuindo a cada mensagem uma chave de categoria que identifica o grupo ao qual pertence. Um agente de mensagens usa essa chave para particionar mensagens em grupos lógicos. Dentro de cada grupo, o broker impõe a ordenação FIFO, de modo que um consumidor que bloqueia um grupo receba as mensagens estritamente na sequência em que foram enfileiradas. Diferentes grupos podem ser processados simultaneamente por diferentes consumidores, o que permite que o sistema escale horizontalmente entre os grupos sem sacrificar a ordenação dentro de cada grupo.

Em Azure, Barramento de Serviço do Azure sessões de mensagem fornecem uma implementação interna desse padrão.

O diagrama a seguir mostra o padrão de comboio sequencial geral.

Diagrama do padrão de comboio sequencial. Ele mostra um produtor, uma fila central e três consumidores.

Na fila, as mensagens para categorias diferentes podem ser intercaladas, conforme mostrado no diagrama a seguir.

Diagrama que mostra quatro categorias de mensagens intercaladas em uma única fila. Cada categoria ocupa sua própria faixa horizontal.

Esse padrão oferece vários benefícios principais:

  • Processamento ordenado por grupo. As mensagens dentro de cada categoria são processadas estritamente em sequência, o que evita condições de corrida, mutações de estado fora de ordem e a necessidade de soluções alternativas para reordenamento.

  • Escala horizontal entre grupos. Cada categoria é uma unidade independente de concorrência. Adicionar consumidores aumenta a taxa de transferência proporcionalmente ao número de categorias ativas, sem comprometer as garantias de ordenação.

  • Desacoplamento produtor-consumidor. Os produtores enfileiram mensagens sem saber qual consumidor as processará ou quando. Os consumidores são escalonáveis e substituíveis de forma independente.

Problemas e considerações

Considere os seguintes pontos ao decidir como implementar esse padrão:

  • Categoria e unidade de escala. Determine qual propriedade das suas mensagens recebidas permite a expansão horizontal. A chave de categoria define a unidade de paralelismo: cada valor de chave distinto se torna um grupo processável independentemente. No cenário de acompanhamento de pedidos, essa propriedade é a ID do pedido. Escolher uma chave muito pouco granular (por exemplo, uma única ID de cliente para todos os pedidos) limita o paralelismo, enquanto escolher uma chave excessivamente granular não proporciona benefícios significativos de ordenação.

  • Limites de taxa de transferência. Avalie a taxa de processamento de mensagens desejada. Como esse padrão impõe o processamento sequencial dentro de cada categoria, a taxa de transferência por categoria é limitada pelo tempo para processar uma única mensagem. Otimize o tempo de processamento por mensagem, por exemplo, usando E/S assíncrona ou gravações downstream em lote, pois esse tempo determina diretamente a taxa de transferência máxima para cada categoria. Se o seu requisito geral de taxa de transferência for muito alto, reconsidere se uma ordenação FIFO estrita é necessária para todo o ciclo de vida da mensagem. As alternativas incluem impor uma mensagem inicial e uma mensagem de término para agrupar uma sequência ou classificar mensagens por carimbo de data/hora dentro de uma janela de lote e, em seguida, enviar o lote para processamento paralelo.

  • Capacidades do serviço. Verifique se o agente de mensagens escolhido oferece suporte ao processamento de mensagens uma de cada vez dentro de uma fila ou de uma categoria de fila. Nem todos os serviços de mensagens fornecem bloqueio em nível de sessão ou garantias FIFO em uma partição. Se o agente não der suporte nativo a essa funcionalidade, o consumidor deverá implementar sua própria lógica de coordenação, o que adiciona complexidade e riscos de processamento duplicado, mensagens perdidas ou execução fora de ordem. O suporte à sessão também pode restringir a escolha da camada de mensagens ou da SKU, o que afeta os custos.

  • Capacidade de evolução. Planeje como você adicionará novas categorias de mensagens ao sistema. O padrão deve acomodar o crescimento da cardinalidade da categoria sem exigir alterações estruturais aos consumidores. Por exemplo, suponha que o sistema de registro contábil descrito anteriormente seja específico para um cliente. Se você precisar integrar um novo cliente, deve ser possível adicionar um conjunto de processadores de razão que distribuam o trabalho por ID de cliente, sem precisar reprojetar a topologia da fila.

  • Entrega de mensagens fora de ordem. As mensagens podem chegar fora de ordem devido à latência variável da rede entre o produtor e o agente, antes que a ordenação de sessão do agente entre em vigor. Considere o uso de números de sequência para verificar a ordenação dentro de cada categoria. Você também pode incluir um sinalizador de fim de sequência na última mensagem de uma transação para que os consumidores possam detectar quando uma sequência é concluída.

  • Manuseio de mensagem suspeita. Uma mensagem que falha repetidamente ao ser processada em uma sessão bloqueia todas as mensagens subsequentes nessa sessão porque o padrão impõe uma ordenação sequencial estrita. Elabore uma estratégia para detectar mensagens suspeitas — como o rastreamento da contagem de tentativas de entrega — e movê-las para uma fila de mensagens mortas após um limite de tentativas definido, permitindo que o processamento das mensagens restantes na sessão continue.

  • Disponibilidade do agente. O broker de mensagens é uma dependência compartilhada para todas as categorias. Sua disponibilidade e durabilidade afetam diretamente as garantias de confiabilidade do padrão. Avalie os recursos de resiliência no nível do agente, como zonas de disponibilidade e recuperação de desastre geográfico com base nos requisitos de disponibilidade e no orçamento da carga de trabalho, pois configurações de maior durabilidade normalmente aumentam o custo.

  • Correção da chave do produtor. O padrão pressupõe que os produtores definam a chave de categoria (ID da sessão) corretamente em cada mensagem. Se um produtor definir uma chave incorreta, acidentalmente ou por causa de um bug, a mensagem roteia para a sessão errada e corrompe o estado desse grupo. Valide se os produtores atribuem chaves de categoria de forma consistente e considere adicionar lógica de validação de chaves no consumidor se a consequência de uma mensagem roteada incorretamente for grave.

  • Complexidade operacional. O monitoramento do processamento baseado em sessões adiciona uma sobrecarga operacional em relação ao consumo padrão de filas. Os operadores precisam de visibilidade sobre o acúmulo de sessões (o número de sessões ativas e o volume de mensagens aguardando em cada sessão) para identificar categorias que estão ficando atrasadas. Sessões com mensagens em fila de mensagens mortas exigem um fluxo de trabalho separado de monitoramento e correção para investigar mensagens que falharam, resolver a causa raiz e reprocessar as mensagens corrigidas na sessão.

  • Contenção e latência do bloqueio de sessão. O bloqueio de sessão introduz uma sobrecarga de latência, pois cada consumidor deve adquirir um bloqueio exclusivo em uma sessão antes de processar mensagens. Quando um consumidor mantém um bloqueio de sessão, nenhum outro consumidor pode processar mensagens dessa sessão, mesmo que o consumidor esteja lento ou temporariamente parado. Se a duração do bloqueio for muito curta, a expiração do bloqueio poderá causar o reprocessamento de mensagens. Se a duração do bloqueio for muito longa, um consumidor paralisado atrasará a recuperação. Ajuste a duração do bloqueio de sessão com base no tempo esperado de processamento de mensagens e implemente a renovação de bloqueio para operações de execução mais longa.

  • Escalabilidade e custo de consumo. O paralelismo entre sessões traduz-se em instâncias de consumidor concorrentes. Em um modelo sem servidor, como Azure Functions, cada sessão ativa é mapeada para uma execução simultânea e, em um modelo dedicado, é mapeada para uma instância ou thread. O número de sessões ativas, portanto, influencia diretamente o custo de computação. Planeje os limites de escalonamento e os controles de concorrência dos consumidores para equilibrar a taxa de transferência em relação ao custo.

Quando usar esse padrão

Use esse padrão quando:

  • As mensagens chegam em ordem e devem ser processadas na mesma ordem.
  • As mensagens podem ser categorizadas para que cada categoria se torne uma unidade independente de escala para o sistema.

Esse padrão pode não ser adequado quando:

  • Você espera cenários de taxa de transferência extremamente alta (milhões de mensagens por minuto), pois o requisito FIFO limita o dimensionamento que o sistema pode alcançar.

  • A ordenação de mensagens não é necessária. Quando as mensagens podem ser processadas independentemente em qualquer ordem, o padrão Consumidores Concorrentes fornece dimensionamento horizontal mais simples sem a sobrecarga de coordenação do bloqueio de sessão.

Design de carga de trabalho

Avalie como usar Sequential Convoy no projeto de uma carga de trabalho para atender às metas e aos princípios apresentados nos pilares do Azure Well-Architected Framework. A tabela a seguir fornece diretrizes sobre como esse padrão dá suporte às metas de cada pilar.

Pilar Como esse padrão apoia os objetivos do pilar
As decisões de design de confiabilidade ajudam sua carga de trabalho a se tornar resiliente ao mau funcionamento e garantir que ela se recupere para um estado totalmente funcional após a ocorrência de uma falha. Este padrão utiliza ordenação FIFO baseada em sessão para eliminar condições de corrida, lógica de processamento de mensagens propensa a contenção e outras soluções alternativas para mensagens fora de ordem que podem levar a falhas de funcionamento.

- RE:02 Fluxos críticos
- RE:07 Trabalhos em segundo plano

Se esse padrão introduzir compensações dentro de um pilar, considere-as em relação aos objetivos dos outros pilares.

Example

No Azure, você pode implementar esse padrão usando Barramento de Serviço sessões de mensagem. Para os consumidores, você pode usar os Aplicativos Lógicos do Azure com o conector peek-lock do Barramento de Serviço ou o Azure Functions com o gatilho do Barramento de Serviço.

Quando um produtor define a SessionId propriedade em uma mensagem, Barramento de Serviço agrupa todas as mensagens que compartilham a mesma ID de sessão em uma única sessão lógica. Um consumidor aceita uma sessão e obtém um bloqueio exclusivo sobre ela. Esse bloqueio garante que apenas um consumidor processe mensagens para essa sessão a qualquer momento e que as mensagens cheguem em ordem FIFO. Outros consumidores podem aceitar e processar simultaneamente diferentes sessões, proporcionando taxa de transferência paralela entre os grupos.

No exemplo de acompanhamento de pedidos, o sistema processa cada mensagem do registro na ordem em que é recebida e envia cada transação para outra fila, onde a categoria é definida como a ID do pedido. Uma transação nunca abrange vários pedidos nesse cenário, portanto, os consumidores processam cada categoria em paralelo, mas na ordem de chegada dentro da categoria.

O processador do razão distribui as mensagens, desempacotando o conteúdo de cada mensagem na primeira fila:

Diagrama da arquitetura de exemplo do Sequential Convoy. Ele mostra um produtor, uma fila de registro, um processador de registro, uma fila de transações e três processadores de pedidos.

O processador do razão executa três etapas:

  1. Avança o razão uma transação de cada vez.
  2. Define a ID da sessão da mensagem para corresponder à ID do pedido.
  3. Envia cada transação do razão a uma fila secundária com a ID da sessão definida como a ID do pedido.

Os consumidores escutam a fila secundária e processam todas as mensagens com IDs de pedido correspondentes na ordem FIFO. Os consumidores usam o modo peek-lock.

A fila do razão é um ponto de transição de serial para paralelo: todas as transações passam por ela sequencialmente antes de serem distribuídas para processamento paralelo baseado em sessões. Esta etapa de serialização é o principal gargalo de escalabilidade, pois limita a taxa de transferência de todo o pipeline subsequente. No entanto, após o processador do razão distribuir as mensagens para a fila secundária, os consumidores podem escalar de forma independente entre as sessões, um para cada ID de pedido.

Tecnologias de suporte

Contribuidores

A Microsoft mantém este artigo. Os colaboradores a seguir escreveram este artigo.

Autor principal:

Para ver perfis de LinkedIn não públicos, entre em LinkedIn.

  • Padrão de consumidores concorrentes: múltiplos consumidores extraem mensagens de uma fila compartilhada em paralelo, o que aumenta a taxa de transferência, mas elimina as garantias de ordenação por mensagem. O padrão Sequential Convoy soluciona o problema de ordenação introduzido por Competing Consumers. Ele resolve essa lacuna particionando mensagens em sessões com chave de categoria e processando cada sessão sequencialmente.

  • Padrão de nivelamento de carga baseado em fila: uma fila atua como buffer entre produtores e consumidores para absorver picos e suavizar cargas irregulares. O padrão de comboio sequencial amplia esse mecanismo de buffering ao adicionar particionamento baseado em sessão, de modo que a fila tanto equilibra a carga entre categorias quanto preserva a ordenação FIFO dentro de cada categoria.

  • Padrão de fila de prioridade: as mensagens são roteadas para filas separadas ou determinada prioridade dentro de uma fila para que o trabalho de prioridade mais alta seja processado antes do trabalho de prioridade mais baixa. Quando a ordem dentro de um nível de prioridade também precisa ser preservada, o padrão de comboio sequencial pode ser combinado com o enfileiramento por prioridade para garantir o processamento FIFO dentro de cada sessão associada a uma prioridade.

  • Mensagem Peek-Lock (leitura não destrutiva): esta operação recupera e bloqueia atomicamente uma mensagem de uma fila ou assinatura para processamento.

  • Como garantir a entrega em ordem de mensagens correlacionadas no Logic Apps usando sessões do Barramento de Serviço: Esta postagem de blog descreve o suporte do Logic Apps ao padrão Comboio Sequencial.