Filas e mensagens pra que te quero

Um comparativo entre Kafka, RabbitMQ e NATS

A filosofia do micro serviço e de partes que funcionam independemente de um todo, mas vivem em constante comunicação, não é nada novo. Essa comunicação pode se dar de várias formas, umas mais onerosas que outras. Por isso, é importante escolher bem para garantir a resiliência e escalabilidade do seu software.

Na Nuveo, estávamos usando uma fila chamada Beanstalkd em parte do sistema. Leve, rápida e com um small footprint dentro do sistema (não precisava instalar nada além da própria fila para apresentar as features que precisávamos), ela serviu muito bem, porém temos dois problemas aí:

  1. Ela não está mais sendo mantida: está estável porém não está mais evoluindo;
  2. Precisamos alterar uma parte do sistema que ainda não usa filas (se comunica pelo banco de dados) para começar a usar filas e assim escalar.

Sendo assim, agora é uma ótima oportunidade para estudar novas tecnologias e ver o que se encaixa melhor pro nosso caso.

Disclaimer

Eu não vou entrar em detalhes como usar e colocar de pé cada fila, mas vou colocar links pra quem tenha interesse nisso. O objetivo desse post é mostrar coisas que encontrei enquanto estava estudando isso.

Definições

Pra começar o estudo definimos algumas coisas que consideramos features necessárias que, idealmente a tecnologia escolhida deverá trazer para o nosso sistema:

Resiliência

Para o nosso sistema é de suma importância que se algo acontecer (alguém puxar a tomada do servidor), as filas consigam se recuperar no lugar de termos que defini-las novamente.

Integridade dos dados

A Nuveo trabalha com jobs (tarefa a ser processada), a fila também não pode perder as mensagens ou jobs antes que essa tarefa tenha sido processada e tenha um resultado.

Small footprint

Como falei antes, já estamos usando uma fila que tem uma pegada pequena dentro do sistema e essa é uma feature que queremos manter, por exemplo, a fila idealmente persistirá as mensagens sem a necessidade de um banco de dados.

Suporte às nossas linguagens de trabalho

Partes do nosso sistema são escritas em Python e Go, então para garantir a integração dessas partes com as filas seria ideal que as filas tenham cliente e/ou bibliotecas nessas linguagens.

Escalabilidade

Um dos motivos para se usar filas é conseguir escalar o sistema sem maiores dificuldades, então quanto mais fácil for acrescentar partes que interagem com as filas (sejam eles consumers ou publishers) melhor.

Ferramentas

Uma das dificuldades de se usar fila é que a complexidade para encontrar bugs e falhas aumenta, então ter ferramentas que facilitem esse trabalho e também o trabalho de monitoramento das filas é algo importante.

Curva de aprendizado e de implementação

Temos uma equipe de profissionais excelente porém o tempo entre apredizado e implementação de uma fila é escasso — isso sem contar a remodelagem que já vai precisar acontecer para que o sistema que é uma mudança de paradigma grande por si só — portanto, aprender como funciona e como colocar a fila no sistema não pode ser muito oneroso.

Maturidade

Esse é o ponto mais subjetivo, para nós a maturidade da tecnologia não vem de quanto tempo essa tecnologia existe, mas principalmente de quem a usa em produção e se existe uma comunidade forte construída em volta dela — quanto mais gente usando, mais gente tem pra encontrar problemas e possíveis soluções — além disso, se essa é uma tecnologia open source ou não e também o quão bem documentada ela está.

As escolhidas para teste

Agora a parte boa! Das milhões de filas disponíveis por aí qual testar? Testar todas é impossível considerando tempo de aprendizado e afins então vamos focar em duas muito famosas RabbitMQ e Apache Kafka e outra que nem tanto, mas muito potente chamada NATS.

RabbitMQ

Com popularidade em alta o RabbitMQ é um broker de mensagens leve e promete ser fácil de fazer deploy. É também amplamente utilizado e consegue alcançar requisitos de grande escala e alta disponibilidade.

Apache Kafka

O Apache Kafka é uma plataforma distribuída de streaming, ou seja, com uma filosofia um pouco diferente do tradicional sistema de troca de mensagens com filas, o foco do Kafka é o stream de eventos, no entanto, essa poderia ser uma possibilidade para a nossa aplicação já que, um dos 4 núcleos do Kafka, que é uma API de Conexão (Connector API) que permitiria escrever conectores para possibilitar que a nossa aplicação se comunicasse com a fila sem ter seu código alterado.

NATS

O NATS Server é um sistema open source de troca de mensagem e provê todo um ecossitema para possibilitar isso. Além disso todas as features do NATS são opcionais, ou seja, você pode escolher usar apenas a parte de event streamming ou só a parte de troca de mensagens com filas (parte que usei aqui).

Considerações finais e resultados

Para rodar os testes com a menor influência de fatores externos possível, todos os testes foram feitos numa máquina nova rodando Debian 9 lá na Digital Ocean.

Para o RabbitMQ utilizei um exemplo de worker/task que está disponível no GitHub, para o NATS o próprio script de benchmark de uma estrutura de pub/sub que vem com o própiro exemplo do go-nats e não foi possível fazer testes de stress com o Apache Kafka. Todas as mensagens enviadas possuíam 16 bytes de tamanho.

No quesito suporte as nossas linguagens, RabbitMQ, NATS e Kafka empatam pois, os três apresentam clientes em Go e em Python.

Tabela comparativa entre o RabbitMQ, Kafka e NATS. LEGENDA: x indica o “não apresenta”; — indica o “não testei”; e o ✓ indica “apresenta”

NATS

De um modo geral, o NATS é muito rápido porém por natureza não persiste as mensagens, parece que existe a possibilidade de fazer isso acontecer usando algum banco de dados, mas não consegui investigar isso mais a fundo.

Ele não possui integração com o DataDog (serviço que utilizamos para monitoramento), mas tem com o Prometheus (outro serviço de monitoramento que poderiamos utilizar).

E por último, a documentação do NATS peca um pouco pois o ecossitema sofreu uma grande mudança e a documentação online não acompanhou. O NATS possui uma curva de aprendizado mais acentuada do que o RabbitMQ no entanto, mais tranquila do que o Apache Kafka.

RabbitMQ

Embora não tão rápido quanto o NATS foi fácil entender e configurar para as mensagens e filas para persistirem em disco. Apenas um parâmetro dentro da função que cria a fila e voilá, a fila se torna persistente e o mesmo se dá para as mensagens.

O RabbitMQ também tem uma ferramenta básica de monitoramento interna e ainda assim possui integração com o DataDog o que facilitou os stress testes.

Por fim, dona de uma ótima documentação o RabbitMQ definitivamente apresenta uma das melhores curvas de aprendizado para quem quer implementar um sistema de filas com troca de mensagem.

Apache Kafka

Por fim, o Kafka foi o teste menos bem sucedido. Foi difícil de colocar para rodar, complicado para entender como interagir, cheio de dependências e apesar de dizer que persistes as mensagens e as filas não consegui fazer isso acontecer no meu teste.

Não consegui chegar nem a testar ferramentas de monitoramento nem fazer os stress testes. Ainda assim, apresenta uma excelente e detalhada documentação e acredito que poderia ser mais bem sucedida no uso do Kafka para aquilo que foi feito: stream de eventos.


Nenhuma das filas tinha todas as features que nós queríamos ou foi possível testá-las em maior profundidade e ainda não decidimos qual delas vamos usar, mas qualquer atualização nessa empreitada eu passo por aqui pra falar sobre. Até a próxima.