Filas

Filas com RabbitMQ

O motor RabbitMQ de Filas oferece mensageria completa para microsserviços: exchanges, routing keys, confirmações e dead-letter. A versão é a 4.4 e o protocolo é AMQP 0-9-1. Para tarefas simples em segundo plano, o motor Valkey pode ser suficiente; veja a comparação em Filas: escolha o motor.

Planos e alta disponibilidade

O RabbitMQ usa os mesmos planos pagos e os mesmos preços das demais Filas: queue-starter, queue-basic, queue-premium e queue-enterprise. Não há plano gratuito para RabbitMQ; o queue-free é só do Valkey. Consulte capacidade e preço no guia de planos.

PlanoNósTipo de fila padrãoMétricas
queue-starter1clássicanão
queue-basic3, com alta disponibilidadequorumnão
queue-premium3, com alta disponibilidadequorumsim
queue-enterprise3, com alta disponibilidadequorumsim

As filas quorum replicam cada mensagem entre os nós e continuam disponíveis se um nó falhar. Nos planos com 1 nó não há réplica: se o nó ficar indisponível, a fila espera ele voltar. A troca de plano não está disponível para o RabbitMQ; para ganhar espaço, aumente o armazenamento. Veja Armazenamento e a API de armazenamento.

Conexão

A conexão usa TLS e tem este formato:

amqps://app:<senha>@rabbitmq-<id>.managed.zenifra.com:<porta>/
  • Usuário: app. Virtual host: /.
  • A senha é exibida apenas na criação e depois de uma rotação de credencial. Depois disso, o Console mostra a URL com a senha mascarada.
  • Fora da Zenifra, somente conexões TLS (amqps) são aceitas.
  • Guarde a URL como variável de ambiente protegida (por exemplo, RABBITMQ_URL) no projeto da aplicação.

Por padrão, o host e a porta são públicos. Você pode desligar o acesso público; nesse caso, o serviço só é alcançado pelas conexões privadas entre aplicações. A resposta de GET /v1/managed-services/:id/connection informa o estado em exposure.

Conexão privada

Aplicações da sua organização ligadas ao RabbitMQ por uma conexão privada usam o endereço interno <projeto>.<id-da-organização>.zenifra.local, na porta 5672 com amqp://; o tráfego fica dentro da rede privada. O passo a passo está em Conexão privada com o RabbitMQ.

Node.js e TypeScript

npm install amqplib
import amqp from 'amqplib'

const connection = await amqp.connect(process.env.RABBITMQ_URL!)
const channel = await connection.createConfirmChannel()

// Exchange de dead-letter e a fila que guarda as mensagens rejeitadas
await channel.assertExchange('orders.dlx', 'direct', { durable: true })
await channel.assertQueue('orders.dead', { durable: true })
await channel.bindQueue('orders.dead', 'orders.dlx', 'created')

// Exchange principal, fila de trabalho e binding por routing key
await channel.assertExchange('orders', 'topic', { durable: true })
await channel.assertQueue('orders.created', {
  durable: true,
  arguments: {
    'x-dead-letter-exchange': 'orders.dlx',
    'x-dead-letter-routing-key': 'created',
  },
})
await channel.bindQueue('orders.created', 'orders', 'orders.created')

// Publicação persistente, com confirmação do servidor
channel.publish('orders', 'orders.created', Buffer.from(JSON.stringify({ id: 42 })), {
  persistent: true,
  contentType: 'application/json',
})
await channel.waitForConfirms()

// Consumo com confirmação manual
await channel.prefetch(10)
await channel.consume('orders.created', async (message) => {
  if (!message) return
  try {
    console.log('process message', JSON.parse(message.content.toString()))
    channel.ack(message)
  } catch {
    channel.nack(message, false, false) // sem reenfileirar: vai para a dead-letter
  }
})

O amqplib valida o certificado do servidor quando a URL usa amqps://. O processamento deve ser idempotente: uma mensagem pode ser entregue mais de uma vez depois de uma falha ou de uma reconexão.

Python

python -m pip install pika
import json
import os
import ssl
from urllib.parse import urlparse

import pika

url = os.environ["RABBITMQ_URL"]
params = pika.URLParameters(url)
params.ssl_options = pika.SSLOptions(ssl.create_default_context(), urlparse(url).hostname)

connection = pika.BlockingConnection(params)
channel = connection.channel()
channel.confirm_delivery()

# Dead-letter
channel.exchange_declare("orders.dlx", exchange_type="direct", durable=True)
channel.queue_declare("orders.dead", durable=True)
channel.queue_bind("orders.dead", "orders.dlx", routing_key="created")

# Exchange principal, fila de trabalho e binding por routing key
channel.exchange_declare("orders", exchange_type="topic", durable=True)
channel.queue_declare(
    "orders.created",
    durable=True,
    arguments={"x-dead-letter-exchange": "orders.dlx", "x-dead-letter-routing-key": "created"},
)
channel.queue_bind("orders.created", "orders", routing_key="orders.created")

# Publicação persistente (a confirmação do servidor é aguardada)
channel.basic_publish(
    "orders",
    "orders.created",
    json.dumps({"id": 42}),
    properties=pika.BasicProperties(delivery_mode=2, content_type="application/json"),
)

# Consumo com confirmação manual
def handle(ch, method, properties, body):
    try:
        print("process message", json.loads(body))
        ch.basic_ack(method.delivery_tag)
    except Exception:
        ch.basic_nack(method.delivery_tag, requeue=False)  # vai para a dead-letter

channel.basic_qos(prefetch_count=10)
channel.basic_consume("orders.created", on_message_callback=handle)
channel.start_consuming()

Exchanges, routing keys e dead-letter

  • Exchange: recebe a mensagem do produtor e a roteia para as filas ligadas a ela. Os tipos mais usados são direct (routing key exata), topic (padrões como orders.*) e fanout (todas as filas).
  • Routing key: etiqueta que o produtor coloca na mensagem e que o binding usa para decidir a fila de destino.
  • Confirmações: o consumidor confirma com ack depois de processar; com nack sem reenfileirar, a mensagem segue para a dead-letter. A confirmação de publicação (waitForConfirms, confirm_delivery) garante que o servidor recebeu a mensagem.
  • Dead-letter: declare a fila com x-dead-letter-exchange (e, se quiser, x-dead-letter-routing-key). As mensagens rejeitadas com nack sem reenfileirar ou expiradas vão para a exchange indicada.

Ao declarar uma fila sem x-queue-type, ela usa o tipo padrão do plano. Redeclarar uma fila já existente com argumentos diferentes causa erro; crie uma fila nova ou remova a antiga.

Limites por plano

PlanoConexõesCanais por conexãoCanais no totalTamanho máximo da mensagemTamanho máximo da fila
queue-starter1001284004 MiB128 MiB
queue-basic3002561.20016 MiB256 MiB
queue-premium1.0005123.00016 MiB512 MiB
queue-enterprise5.0001.02412.00016 MiB2 GiB

Estes são os limites atuais e podem mudar. "Canais no total" soma os canais de todas as conexões; ao passar dele, o servidor recusa o novo canal. Quando uma fila atinge o tamanho máximo, novas publicações nela são rejeitadas. Quando a memória do serviço enche, as publicações ficam bloqueadas até que os consumidores esvaziem mensagens; mantenha consumidores ativos e acompanhe as métricas.

Métricas

Nos planos Premium e Enterprise, a página do projeto mostra filas, mensagens prontas e sem confirmação, consumidores, taxas de publicação e de entrega e um aviso quando a memória está cheia. Mensagens sem confirmação que crescem indicam consumidores lentos ou sem ack.

Backups

O backup automático está incluído no plano, com restauração pelo suporte. Veja Backups automáticos.

O que o backup garante, até o último backup realizado:

  • as mensagens em filas persistentes (duráveis ou quorum);
  • a configuração: exchanges, filas, bindings, usuários e políticas.

O que não é garantido:

  • mensagens publicadas depois do último backup;
  • mensagens em filas não persistentes.

Para não perder mensagens, use filas duráveis, publique com persistent e confirmação de publicação e mantenha consumidores idempotentes.

Perguntas frequentes

Posso conectar sem TLS?

Não. Fora da Zenifra, somente amqps:// é aceito.

Posso usar vários virtual hosts ou usuários?

A conexão fornecida usa o virtual host / e o usuário app.

O RabbitMQ garante entrega exactly-once?

Não. A entrega é at-least-once; use uma chave de idempotência na aplicação.

Próximos passos

Última atualização em

Nessa página