RabbitMQ

O RabbitMQ é um broker de mensagens MQTT, AMQP, STOMP e WebSTOMP (STOMP via WebSockets) e cliente JMS (Java Message Service) feito na linguagem de programação Erlang e mantido pela empresa Pivotal (a mesma que fez o Spring, famoso framework web para Java)

Como o RabbitMQ funciona? O vídeo abaixo explica muito bem.

https://youtu.be/deG25y_r6OY

Instalando RabbitMQ no Debian/Ubuntu Linux

Há várias maneiras de instalar o RabbitMQ no Linux. Os tutoriais oficiais não são tão simples. Aqui vou apresentar quatro delas.

Recomendo muito instalar o RabbitMQ de duas maneiras: via apt ou via docker, porque são as opções mais fáceis. As outras opções são bem mais complicadas e demandam tempo.

Instalação fácil via apt-get
(versão desatualizada)

Você pode usar o comando apt-get para instalar o RabbitMQ. O problema é que geralmente são versões antigas.

apt install rabbitmq-server

O problema em se instalar dessa forma é que a versão instalada muito provavelmente é antiga e pode conter alguns bugs. Se você estiver OK com isso, sem problemas.

Instalação manual via dpkg (versão que você quiser)

Nesse modo você precisa instalar manualmente o Erlang e depois o RabbitMQ. Tome cuidado com isso, pois a versão escolhida do Erlang e do RabbitMQ precisam ser compatíveis. Veja aqui a lista de compatibilidades.

Instalação via apt-get pelo repositório do RabbitMQ (última versão)

Tentei seguir esse tutorial na documentação oficial mas não consegui. Foi bem sofrido.

Há um script pronto que faz a instalação automática aqui. Não cheguei a testar.

Instalação via Docker (versão que você quiser)

Instale a última versão do docker. Pra isso você precisa adicionar o repositório oficial do Docker. Esse tutorial da Digital Ocean é excelente.

Pronto, o Docker está instalado e o status do serviço do docker deve aparecer na tela.

Agora vamos instalar o RabbitMQ!

Os conteiners docker oficiais do time do RabbitMQ estão nesse link.

docker run -d --hostname rabbitmq --log-driver=journald --name rabbitmq --restart unless-stopped --network host rabbitmq:management

Neste comando fazemos:

  • Criamos um contêiner docker com hosname rabbitmq e nome do container rabbitmq
  • Instalamos no contêiner da última versão do RabbitMQ disponível no docker hub com o plugin management ativo (plugin para gerenciar o RabbitMQ via interface web)
  • Permitimos que todas as portas de dentro do conteiner sejam acessíveis pelo computador hospedeiro
  • O RabbitMQ será reiniciado se o programa cair ou se o computador for reiniciado. Caso o RabbitMQ seja parado manualmente, ele só será reiniciado automaticamente se ele for iniciado manualmente em seguida

Pronto! Acesse o gerenciador do RabbitMQ em localhost:15672

Como acessar o RabbitMQ terminal pelo docker?

docker exec -it rabbitmq bash

Vendo logs do RabbitMQ pelo systemd no computador hospedeiro

# -f para mostrar mensagens em fluxosudo journalctl -f CONTAINER_NAME=rabbitmq# -b para apenas mensagens desde o último bootsudo journalctl -b CONTAINER_NAME=rabbitmq# -o json para mostrar log em formato jsonsudo journalctl -o json CONTAINER_NAME=rabbitmq

Requisitos de Hardware e Consumo

Quanto consome o RabbitMQ via docker?

Ubuntu 18 x64 em uma VirtualBox com 1.45 gb de memóra RAM.

Ubuntu 18 x64 sem nada instalado

  • Consumo memória RAM - 83.4M/1.45G
  • 0,7% CPU

Pós instalação Docker

  • Consumo memória RAM - 138MB/1.45GB
  • Consumo HD 243MB
  • Download de 50.7 MB

Pós instalação RabbitMQ no Docker

  • Consumo memória RAM - 257MB/1.45GB
  • Consumo HD ?

Quais portas o RabbitMQ usa?

  • 5672, 5671: Portas mais comuns (AMQP), usadas para editar, acessar, publicar e se inscrever em filas AMQP 0-9-1 e 1.0 com e sem criptografia TLS
  • 1883, 8883: Portas MQTT, usadas para acessar via MQTT com e sem criptografia TLS, se o MQTT plugin estiver atualizado
  • 15672: HTTP API clients, management UI and rabbitmqadmin (only if the management plugin is enabled)
  • 61613, 61614: STOMP clients without and with TLS (only if the STOMP plugin is enabled)
  • 15674, 15673?: STOMP-over-WebSockets clients (ativo apenas se o plugin Web STOMP plugin estiver habilitado)
  • 15675, 15673?: MQTT-over-WebSockets sem ou com TLS (ativo apenas se o plugin Web MQTT plugin estiver habilitado)
  • 4369: epmd, a peer discovery service used by RabbitMQ nodes and CLI tools
  • 25672: used for inter-node and CLI tools communication (Erlang distribution server port) and is allocated from a dynamic range (limited to a single port by default, computed as AMQP port + 20000). Unless external connections on these ports are really necessary (e.g. the cluster uses federation or CLI tools are used on machines outside the subnet), these ports should not be publicly exposed. See networking guide for details.
  • 35672-35682: used by CLI tools (Erlang distribution client ports) for communication with nodes and is allocated from a dynamic range (computed as server distribution port + 10000 through server distribution port + 10010). See networking guide for details.
  • 15692: Prometheus metrics (only if the Prometheus plugin is enabled)

Referência:
https://www.rabbitmq.com/networking.html

Habilitando plugins no RabbitMQ

O RabbitMQ conta com alguns plugins que aumentam suas funcionalidades. Para ver os plugins disponíveis e instalá-los:

# Listando todos os plugins disponíveis, ativos e inativosrabbitmq-plugins list

# Habilitando um pluginrabbitmq-plugins enable plugin-name

# Desabilitando um pluginrabbitmq-plugins disable plugin-name

Integrando MQTT com o RabbitMQ

Você pode enviar mensagens MQTT diretamente para o RabbitMQ.

Para isso, habilite o plugin rabbitmq_mqtt

rabbitmq-plugins enable rabbitmq_mqtt

Todas as mensagens recebidas via MQTT são recebidas pela exchange amq.topic. Você pode mudar essa exchange no arquivo de configuração.

Particularidades do MQTT dentro do RabbitMQ (QoS e persistência)

O RabbitMQ não funciona com QoS2, ou seja, não é possível garantir que uma mensagem chege exatamente uma vez no broker. Quando o RabbitMQ recebe uma mensagem com QoS2 ela a trata automaticamente como QoS1.

Sobre persistência de mensagens, em uma fila dotipo Durable, mensagens com QoS1 e QoS2 são automaticamente salvas no HD, ou seja, em caso de falha no RabbitMQ e restart do servidor RabbitMQ ou do sistema operacional em si, as mensagens não serão perdidas. Em contrapartida, com QoS0 as mensagens existem apenas na memória RAM, em caso de falha e restart, as mensagens são perdidas.

Como salvar mensagens do tipo topic (amq.topic) em uma queue?

  • Crie uma queue
  • Faça um binding na queue criada com a exchange amq.topic
  • Decida e insira a binding key

Os tópicos do MQTT se traduzem para os tópicos do RabbitMQ da seguinte forma:

MQTTRabbitMQ
casa/sala-de-estar/lampadacasa.sala-de-estar.lampada
casacasa
casa/#casa.#
##

E como as mensagens são salvas na fila dependendo do tópico?

TópicoResultado
#Captura todas as mensagens recebidas.
casa.#Captura todas as mensagens para casa, incluindo as aninhadas (casa.quarto, casa.banheiro, casa.quarto.lampada...).
casaCaptura todas as mensagens para casa, somente!
*.salaCaptura todas as mensagens para a sala (no segundo nível).

Como fazer vários servidores RabbitMQ trabalharem juntos?

Há 3 formas de criar um sistema distribuído com servidores Rabbit:

  • Cluster
  • Federation
  • Shovel

Shovel

O Shovel é a maneira mais simples de fazer vários servidores trabalharem juntos com o RabbitMQ.

  • Ao fazer um shovel e não definir uma routing key de saída, o shovel utilizará automaticamente a routing key original da mensagem.
  • Ao criar um shovel é possível ver um consumer na queue a ser propagada para outro local.
  • O shovel poderá duplicar mensagens. Isso é uma limitação da implementação do AMQP pelo RabbitMQ. Apesar disso o RabbitMQ consegue garantir que nenhuma mensagem será perdida.

Criando uma imagem Docker com certos plugins pré instalados

Crie uma pasta nova e dentro dela um arquivo chamado Dockerfile com o seguinte:

FROM rabbitmq:3.7-managementRUN rabbitmq-plugins enable rabbitmq_shovel &&\    rabbitmq-plugins enable rabbitmq_shovel_management &&\    rabbitmq-plugins enable rabbitmq_mqtt

Este arquivo irá instalar o RabbitMQ 3.7 com os plugins:

  • Management (interface gráfica)
  • Shovel
  • Shovel Management (interface gráfica para o shove)
  • RabbitMQ MQTT

Estando dentro da pasta execute:

docker build -t nome-da-sua-imagem-customizada .# o . indica para o docker verificar a pasta atual pelo Dockerfile

Para instalar essa imagem customizada do RabbitMQ

docker run -d --hostname rabbitmq --name rabbitmq --restart unless-stopped -p 15672:15672 -p 5672:5672 -p 5671:5671 -p 1883:1883 nome-da-sua-imagem-customizada

Publicando mensagens com AMQP

Ao publicar uma mensagem no RabbitMQ via AMQP, você pode atribuir alguns parâmetros no cabeçalho da mensagem. Estes parâmetros são:

  • Nome da Exchange (string)
  • Routing Key (string)
  • Mandatory (verdadeiro ou falso)
  • Immediate (verdadeiro ou falso)
  • Delivery Mode (1 = não-persistente, 2=persistente)
  • App ID (string)
  • Cluster ID (string)
  • Content Encoding (string)
  • Content Type (string)
  • Correlation ID (string)
  • Expiration (string)
  • Headers? (uma lista com chave e valor)
  • Message ID (string)
  • Priority (int)
  • Reply To (string)
  • Timestamp (data)
  • Type (string)
  • User ID (string)

Documentação sobre headers do AMQP no RabbitMQ
https://www.rabbitmq.com/amqp-0-9-1-reference.html

Sobre a flag Mandatory
https://jack-vanlightly.com/blog/2017/3/10/rabbitmq-the-different-failures-on-basicpublish#:~:text=The%20mandatory%20flag%20tells%20RabbitMq,when%20an%20ack%20is%20received.

Sobre a flag Mandatory e Immediate
https://stackoverflow.com/questions/6386117/rabbitmq-use-of-immediate-and-mandatory-bits

Tutoriais:
https://docs.vmware.com/en/vCloud-Availability-for-vCloud-Director/1.0.1/com.vmware.vcavcd.install.doc/GUID-AC63692F-0288-4F49-8D4D-9F837D1E4FE5.html
https://success.outsystems.com/Support/Enterprise_Customers/Maintenance_and_Operations/Cache_Invalidation_in_OutSystems_11/Enable_TLS_communication_in_RabbitMQ
https://www.rabbitmq.com/ssl.html#enabling-tls

Referências

Arquivo de configuração do RabbitMQ
https://www.rabbitmq.com/configure.html#configuration-file

Documentação sobre TLS no RabbitMQ
https://www.rabbitmq.com/ssl.html

Troubleshooting TLS no RabbitMQ
https://www.rabbitmq.com/troubleshooting-ssl.html

Configurando Dynamic Shovel
https://www.rabbitmq.com/shovel-dynamic.html

Configurando Shovel
https://www.rabbitmq.com/shovel.html

URI query Parameters
https://www.rabbitmq.com/uri-query-parameters.html

Referências

Instalação do RabbitMQ

Instalando RabbitMQ no Debian/Ubuntu
https://www.rabbitmq.com/install-debian.html#package-dependencies

Instalando RabbitMQ via Docker
https://hub.docker.com/_/rabbitmq/

Instalando RabbitMQ
https://www.vultr.com/docs/how-to-install-rabbitmq-on-ubuntu-16-04-47

Criando clusters

Documentação oficial sobre comunicação entre brokers distribuídos
https://www.rabbitmq.com/distributed.html

Documentação oficial sobre o Federation Plugin
https://www.rabbitmq.com/federation.html

Documentação oficial sobre o Shovel Plugin
https://www.rabbitmq.com/shovel.html

Qual a diferença entre Shovel e Federation?
https://stackoverflow.com/questions/19357272/when-to-use-rabbitmq-shovels-and-when-federation-plugin

Documentação oficial sobre criação de clusters
https://www.rabbitmq.com/clustering.html

RabbitMQ e garantia de entrega

Sobre garantia de entrega do RabbitMQ e semântica
https://www.rabbitmq.com/semantics.html

Sobre confirmações de mensagens (publishers e consumers)
https://www.rabbitmq.com/confirms.html

Publisher Confirms (Java)
https://www.rabbitmq.com/tutorials/tutorial-seven-java.html

Tutorial RabbitMQ Java
https://www.rabbitmq.com/api-guide.html

Documentação do RabbitMQ

Documentação da Pivotal
https://rabbitmq.docs.pivotal.io/37/index.html

Tipos de Exchanges e seus comportamentos

Artigo da Lisa Smith sobre tipos de exchanges
https://www.compose.com/articles/configuring-rabbitmq-exchanges-queues-and-bindings-part-1/

Exemplo de arquivo de configuração do RabbitMQ rabbitmq.conf

# Disabling guest user from non localhost (loopback) interfacesloopback_users.guest = false###### AMQP Configurations ######listeners.tcp.default = 5672listeners.ssl.default = 5671ssl_options.cacertfile = /etc/rabbitmq/my.ca_bundlessl_options.certfile = /etc/rabbitmq/my.crtssl_options.keyfile = /etc/rabbitmq/my.key# ssl_options.verify = verify_peerssl_options.verify = verify_none# ssl_options.fail_if_no_peer_cert = truessl_options.fail_if_no_peer_cert = false# Disabling TLS versions older than 1.1ssl_options.versions.1 = tlsv1.2ssl_options.versions.2 = tlsv1.1###### MQTT Configurations ######mqtt.listeners.tcp.default = 1883## Default MQTT with TLS port is 8883mqtt.listeners.ssl.default = 8883# anonymous connections, if allowed, will use the default# credentials specified heremqtt.allow_anonymous  = false# mqtt.default_user     = guest# mqtt.default_pass     = guestmqtt.vhost            = /mqtt.exchange         = amq.topic# 24 hours by defaultmqtt.subscription_ttl = 86400000# To disable# mqtt.subscription_ttl = undefinedmqtt.prefetch         = 10# Dealing with MQTT Retained Message## use DETS (disk-based) store for retained messages# mqtt.retained_message_store = rabbit_mqtt_retained_msg_store_dets## only used by DETS store#mqtt.retained_message_store_dets_sync_interval = 2000## use ETS (ram-based) store for retained messages# mqtt.retained_message_store = rabbit_mqtt_retained_msg_store_ets# Application specific MQTT# mqtt.listeners.tcp.1 = 127.0.0.1:1883# mqtt.listeners.tcp.2 = ::1:1883# mqtt.tcp_listen_options.backlog = 4096# mqtt.tcp_listen_options.recbuf  = 131072# mqtt.tcp_listen_options.sndbuf  = 131072# mqtt.tcp_listen_options.keepalive = true# mqtt.tcp_listen_options.nodelay   = true# mqtt.tcp_listen_options.exit_on_close = true# mqtt.tcp_listen_options.send_timeout  = 120###### Management Configurations ######management.tcp.port = 15672management.ssl.port       = 15671management.ssl.cacertfile = /etc/rabbitmq/my.ca_bundlemanagement.ssl.certfile   = /etc/rabbitmq/my.crtmanagement.ssl.keyfile    = /etc/rabbitmq/my.key###### Web MQTT Configurations ######web_mqtt.tcp.port = 15675web_mqtt.ssl.port       = 15673web_mqtt.ssl.backlog    = 1024web_mqtt.ssl.cacertfile = /etc/rabbitmq/my.ca_bundleweb_mqtt.ssl.certfile   = /etc/rabbitmq/my.crtweb_mqtt.ssl.keyfile    = /etc/rabbitmq/my.key###### Web Stomp Configurations ######web_stomp.tcp.port = 15674web_stomp.ssl.port       = 15676web_stomp.ssl.backlog    = 1024web_stomp.ssl.cacertfile = /etc/rabbitmq/my.ca_bundleweb_stomp.ssl.certfile   = /etc/rabbitmq/my.crtweb_stomp.ssl.keyfile    = /etc/rabbitmq/my.key# WebSocket traffic compression is enabled by defaultweb_stomp.ws_opts.compress = true# WebSocket connection inactivity timeout# web_stomp.ws_opts.idle_timeout# web_stomp.ws_opts.max_frame_size = 50000# connection inactivity timeout# web_stomp.cowboy_opts.idle_timeout = 60000# max number of pending requests allowed on a connection# web_stomp.cowboy_opts.max_keepalive = 200# max number of headers in a request# web_stomp.cowboy_opts.max_headers   = 100# max number of empty lines before request body# web_stomp.cowboy_opts.max_empty_lines = 5# max request line length allowed in requests# web_stomp.cowboy_opts.max_request_line_length

Mudando a porta do MQTT no RabbitMQ

Crie/edite o arquivo /etc/rabbitmq/rabbitmq.config (configuração antiga)

[  {rabbitmq_mqtt, [{tcp_listeners, [12345]}]}].

Arquivo /etc/rabbitmq/rabbitmq.conf padrão:

loopback_users.guest = falselisteners.tcp.default = 5672management.tcp.port = 15672

You should also read:

NGINX + RabbitMQ + Websocket

Fazendo proxy reverso websocket com nginx, sem TLS server { listen 3000; server_name ws.ubivis.io; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host;…