Garantias de ordem de mensagens
1) O que é «ordem» e o que é necessário
A ordem das mensagens é a relação «o que deve ser processado antes» para eventos de uma entidade (pedido, usuário, carteira) ou para todo o fluxo. Ele é importante para invariantes, como «status A antes B», «saldo antes de cancelamento», «versão n antes n + 1».
Em sistemas distribuídos, a ordem total global das estradas é raramente necessária; Normalmente, há ordem local suficiente para a chave.
2) Tipos de garantia da ordem
1. Per-partition (ordem local na secção de logs) - Kafka: a ordem dentro da partição é mantida, e não há ordem entre as partições.
2. Per-key (ordering key/mensagem group) - Todas as mensagens com uma única chave são rotuladas em um único fluxo de processamento (Kafka key, SQS FIFO MessageGroupId, Pub/Sub ordering key).
3. Global total order - Todo o sistema vislumbra uma ordem única (registro/sequenciador distribuído). Caro, piora a disponibilidade e throughput.
4. Causal order - «evento B pós A, se B observar o efeito A». Alcançável através de metadados (versões, tempo Lamport/relógio vetorial) sem um sequenciador global.
5. Best-effort order - O corretor tenta manter a ordem, mas pode haver mudanças em casos de falhas (muitas vezes no NATS Core, RabbitMQ em vários consórcios).
3) Onde a ordem é quebrada
Consoantes paralelos de uma fila (RabbitMQ: múltiplos consumidores por fila → interleaving).
Retrai/reaproveitamento (at-least-once), timeouts 'ack', nova produção para a fila.
Rebalance/feelover (Kafka: mudança de partido/líder).
DLQ/reexaminação - A mensagem «venenosa» vai para o DLQ, os seguintes vão além → quebra lógica.
A região multi-regional e a replicação são diferentes atrasos → rascronização.
4) Design «ordem à chave»
A chave forma uma unidade de ordenamento. Recomendações:- Use as chaves naturais: 'order _ id', 'wallet _ id', 'agregate _ id'.
- Vigie as chaves quentes - uma chave pode «bloquear» o fluxo (head-of-line blocking). Se necessário, use a chave 'order _ id # shard (0... k-1)' com a reconstrução da ordem em sinca.
- No Kafka, uma chave → uma partitura, a ordem será mantida dentro da chave.
java producer.send(new ProducerRecord<>("orders", orderId, eventBytes));
(chave = 'orderId' garante ordem local.)
5) Ordem contra largura de banda
Garantias fortes muitas vezes conflitam com throughput e acessibilidade:- Um consumer por fila mantém a ordem, mas reduz o paralelismo.
- At-least-once + paralelismo aumentam a produtividade, mas exigem idempotação e/ou restabelecimento da ordem.
- O Global order adiciona hop ao sequenciador → ↑latentnost e risco de falha.
Comprometimento: ordem per-key, paralelismo = número de partições/grupos, + sinos idumpotentes.
6) Controle de ordem em corretores específicos
Kafka
A ordem está dentro da partição.
Cumpra 'max. in. flight. requests. per. connection ≤ 5` с `enable. idempotence = true 'para que os retais do produtor não mudem a ordem.
Um grupo de consórcios, uma partitura → um worker no momento. Novas entregas são possíveis → mantenha sequence/versão na camada de negócios.
As transações (read-processo-write) mantêm a coerência «leu/gravado/ofmitil», mas não criam uma ordem global.
properties enable.idempotence=true acks=all retries=2147483647 max.in.flight.requests.per.connection=5
RabbitMQ (AMQP)
A ordem é garantida em uma fila para um único consumer. Com vários conselheiros de mensagens, pode haver um «deslize».
Para ordem, um consumer ou prefetch = 1 + ack após a conclusão. Para paralelismo, divida as filas por chave (sharding exchanges/consistent-hash exchange).
NATS / JetStream
NATS Core - best-effort, baixa latência, ordem pode ser perturbada.
JetStream: ordenamento dentro de um strim/seqüência; em casos raros, é possível remarcar o console → use sequence e tampão de recuperação.
SQS FIFO
Exactly-once processing (efetivamente, com dedução) e ordem dentro do MessageGroupId. Paralelismo é o número de bandas dentro da banda head-of-line.
Google Pub/Sub
Ordering key dá ordem dentro da chave; Se houver erro, a publicação será bloqueada antes da recuperação - acompanhe o backpressure.
7) Pattern de preservação e restabelecimento da ordem
7. 1 Sequence/versioning
Cada evento traz 'seq '/' version'. Consumer:- só aceita um evento se 'seq = last _ seq + 1';
- senão, coloque no tampão de espera até que os faltosos cheguem ('last _ seq + 1').
pseudo if seq == last+1: apply(); last++
else if seq > last+1: buffer[seq] = ev else: skip // дубль/повтор
7. 2 Tampões e janelas (stream processing)
Time-window + watermark: Aceitamos out-of-order dentro da janela, por watermark «fechamos» a janela e organizamos.
Allowed lateness: canal para atrasados (recompute/ignore).
7. 3 Sticky-routing por chave
O hash 'hash (key)% shards' envia todos os eventos da chave para o mesmo worker.
Em Kubernetes - mantenha a sessão (sticky) no nível de fila/lã, não no balanceador L4 HTTP.
7. 4 Ator-modelo/« um fluxo por chave »
Para as unidades críticas (carteira): O acionista processa em sequência, o resto do paralelismo é o número de acções.
7. 5 Idempotidade + reordering
Mesmo com a ordem restabelecida, pode haver repetições. Combine UPSERT + versão e Inbox (consulte «Exactly-once vs At-least-once»).
8) Trabalhar com mensagens «venenosas» (poison pills)
Manter a ordem enfrenta o desafio de «como viver se uma mensagem não for processada?»
Ordem rigorosa: bloqueio do fluxo de chave (SQS FIFO: grupo inteiro). A solução é by-key DLQ: apenas uma chave/grupo problemático para uma fila/anistia manual.
Ordem flexível: permitindo omissão/compensação; logeamos e continuamos (não para as finanças/unidades críticas).
Política de retrações: «max-deliver» + backoff + efeitos avidompotentes limitados.
9) Região multi e sistemas globais
Cluster-linking/replicação (Kafka) não garante a ordem global interregional. Priorize a ordem local per-key e os sinos idempotados.
Para truly-globorder, use o sequenciador (logs centrais), mas isso afeta a disponibilidade (CAP: menos A em quebras de rede).
Alternativa: causal order + CRDT para alguns domínios (contadores, múltiplos) - não precisa de ordem rigorosa.
10) Observabilidade da ordem
Метрики: `out_of_order_total`, `reordered_in_window_total`, `late_events_total`, `buffer_size_current`, `blocked_keys_total`, `fifo_group_backlog`.
11) Anti-pattern
Uma fila + muitos consoantes sem chumbar a chave - a ordem é quebrada imediatamente.
Retrai através do peer-pablish na mesma fila sem idempotency - duplos + out-of-order.
A ordem global «por precaução» é uma explosão de laticínios e custos sem benefícios reais.
SQS FIFO um grupo para tudo - um head-of-line completo. Use a chave MessageGroupId per.
Ignorar «chaves quentes» - uma «carteira» trava tudo; divida a chave por baixo das chaves onde puder.
Misturar fluxos críticos e bulk em uma fila/grupo é um efeito mútuo e perda de ordem.
12) Folha de cheque de implementação
- O nível de garantia definido é: per-key/per-partition/causal/global?
- A chave de ordenamento foi projetada e a estratégia contra «chaves quentes».
- O roteador está configurado: particionamento/ GroupId/ordering key.
- Os consórcios estão isolados por chave (sticky-routing, shard-workers).
- Idempotidade ativada e/ou Inbox/UPSERT incluídos nos sinks.
- Implementado sequence/version e tampão de reordering (se necessário).
- Política DLQ by key e retrai com backoff.
- Métricas de ordem e alertas: out-of-order, blocked _ keys, late _ events.
- Game day: revalidação, perda de nó, mensagem «venenosa», atrasos de rede.
- Documentação: invariantes da ordem, limites das janelas, influência sobre a SLA.
13) Exemplos de configuração
13. 1 Kafka Consumer (minimizar perturbação da ordem)
properties max.poll.records=500 enable.auto.commit=false # коммит после успешной обработки батча isolation.level=read_committed
13. 2 RabbitMQ (ordem ao custo do paralelismo)
Um consumer para a fila + 'basic. qos(prefetch=1)`
Para o paralelismo, várias filas e hash-exchange:bash rabbitmq-plugins enable rabbitmq_consistent_hash_exchange публикуем с хедером/ключом для консистентного хеша
13. 3 SQS FIFO
Especifique o MessageGroupId = key. Paralelismo = número de grupos.
MessageDeduplicationId para proteção contra suplentes (na janela do provedor).
13. 4 NATS JetStream (ordered consumer, esboço)
bash nats consumer add ORDERS ORD-KEY-42 --filter "orders.42.>" --deliver pull \
--ack explicit --max-deliver 6
14) FAQ
Preciso de uma ordem global?
Quase nunca. Quase sempre o suficiente para-key. A ordem global é cara e bate na disponibilidade.
Como estar com uma mensagem «venenosa» com uma ordem rigorosa?
A: Traduzir apenas sua chave/grupo para DLQ, o resto é continuar.
Q: É possível obter ordem e escala ao mesmo tempo?
A: Sim, ordem para a chave + muitas chaves/partituras + operações idumpotentes e tampões de reordering onde você quiser.
O que é mais importante, ordem ou exactly-once?
A: Para a maioria dos domínios - ordem por chave + efeitos efetivamente exactly-once (idempotidade/UPSERT). Transporte pode ser at-least-once.
15) Resultado
A ordem é uma garantia local em torno da chave de negócios, não uma disciplina global cara. Projete chaves e partituras, limite chaves «quentes», use idempotidade e, onde você quiser, sequence + buffer reordering. Acompanhe as métricas "out-of-order" e "blocked keys', teste as falhas - e você terá um processamento previsível sem sacrifícios de produtividade e disponibilidade.