O padrão Observable veio reduzir o acoplamento entre os vários domínios da aplicação. Contudo, especialmente com o crescimento da demanda, outros problemas surgiram. E o mesmo padrão, com algumas modificações, acabou se apresentando como uma opção capaz de prover assincronicidade e desacoplamento físico de processos. Vamos entender melhor quais problemas e como conceitos como mensageria, evento, filas e tópicos podem resolvê-los.
Entendendo o contexto
Quando comecei a programar, meus sistemas eram geralmente mono-usuários. Quando muito, 10, 12 acessos simultâneos e na mesma rede. Com o crescimento das empresas, esse número começou a aumentar. Foi no início do século que escrevi meu primeiro sistema multi-filiais. Nós mantínhamos uma máquina dedicada dentro do provedor de internet, expondo um servidor de aplicação que se comunicava com as filiais via SOAP. Para a época, algo revolucionário! Eu não posso expor números, mas eram muitos acessos simultâneos, com inserções de dados, emissão de longos relatórios. Funcionava rápido e perfeito – e tinha cache para quando a internet ficasse off-line.
Hoje esse cenário é diferente. Imagine as empresas de maquininha de cartão. Toda transação faz uma requisição para uma série de sistemas, que estão espalhados na nuvem. Todo o processo precisa ocorrer em poucos segundos, conversando com os bancos e operadoras de todos os cartões. Já imaginou quantas requisições por segundo são feitas para esses sistemas? Agora imagine se para cada requisição fosse necessário esperar a conclusão de uma outra em curso? Imagine, também, se para cada requisição fosse necessário subir na memória o sistema completo: desde a autorização de transações até o cadastro de clientes; partes do sistema que absolutamente não tem ligação nenhuma com a requisição feita.
Para ser sincero, o que acontece na maior parte das vezes é que realmente esperamos as transações ocorrerem uma após a outra. Temos threads que ajudam a “paralelizar” o processamento, temos loadbalances que nos ajudam a distribuir o tráfego entre várias instâncias do mesmo sistema. Mas para reduzir custos (afinal, sistemas na memória consomem recursos e recurso tem custo), o ideal é que esses sistemas sejam o menor possível e concluam sua execução o mais rápido possível. É em contextos como esse que a mensageria brilha!
Mensageria: o que é isso?
Até agora eu usei o termo “mensageria” de maneira genérica, representando toda comunicação intermediada. Mas mensageria tem o seu significado próprio. Confira:
Tente imaginar que você está trabalhando em um escritório. E que precisa ler e processar um tipo específico de documento. Você consegue processar um documento a cada cinco minuto. O que acontece se chegar um documento a cada seis minutos? Você consegue garantir suas entregas com perfeição. Mas e se você começar a receber um documento por minuto? As pessoas vão ficar ali, paradas, esperando você pegar o documento, processá-lo e só então dar prosseguimento na sua vida. Então você tem uma brilhante ideia: “Gente, coloquem os documentos aqui no escaninho que, assim que possível, eu vou processá-los para vocês. Ok?”
Essa analogia de deixar o documento num “escaninho digital” define muito bem o que chamamos de mensageria. E assim, ao invés de ter eventuais problemas de timeout – porque nenhuma requisição quer esperar cinco minutos para retornar – você garante o processamento dos documentos (ou dos dados) que precisam ser processados. Mensageria, então é: uma forma de garantir o processamento de uma alta demanda de solicitações, mesmo com uma baixa vazão (throughput).
Eventos: o que é isso?
Se você perguntar para alguém que não trabalha com Sistemas de Informação qual é o evento na frase “Quando a chuva passar, eu irei ao cinema”, com certeza dirão que é ir ao cinema. Mas para as pessoas desenvolvedoras, o evento é “a chuva passar”.
Eventos são gatilhos disparados dentro da aplicação por conta da alteração de estado de alguma entidade. Ela pode ter sido alterada, modificada, apagada ou criada. Uma venda, por exemplo: “Quando a transação do cartão for aprovada, o setor financeiro vai emitir a nota fiscal, e enquanto isso, o setor de logística vai preparar o envio da mercadoria”.
Perceba que, no caso de eventos, eu não estou preocupado apenas com a vazão das informações. Mais que isso, minha preocupação – igual quando discutimos o padrão observable – é de informar, para toda solução, que um determinado evento ocorreu. Desta forma, dou chance aos módulos de reagirem adequadamente àquele evento: seja fazendo ações ou desfazendo processamentos.
Queues x Topics
A diferença entre filas e eventos é bastante sutil. Compreendo a mensageria como sendo quase que um intermediário entre chamadas remotas (RPC). Ela é mais evidente numa relação um-para-um, mas também pode ocorrer numa relação muitos-para-um. Por sua vez os eventos podem inverter essa lógica, representando relação n-para-n. Para que essas diferenças fiquem claras na arquitetura, fazemos uso dos conceitos de Queues (filas) e Topics (tópicos).
Assim, para mensageria nós temos as filas (queues). Elas podem ser efêmeras ou persistentes, sendo que para o contexto de messaging queue, a efemeridade faz mais sentido. As filas servem para enfileirar as chamadas para um serviço. Pense na fila como o escaninho onde você deixa um documento para ser processado pelo servidor mais tarde. A missão do broker para a fila é entregar a mensagem para apenas um recebedor e não mais que um. Imagine se dois processos trabalhassem a mesma mensagem? Teríamos duplicidade de informações (duplicidade de entregas, duplicidade de depósitos, duplicidade de… problemas!).
Já os Tópicos têm por característica serem persistentes. Isso porque a mensagem – que nós podemos chamar de evento também – pode ser entregue mais de uma vez e para aplicações diferentes. Essa persistência pode se dar por um período determinado, com a criação opcional de snapshots, ou ser “eterna”, o que particularmente, não aconselho. Se realmente quiser manter o registro dos eventos, utilize bancos de dados para isso. Pense nos tópicos como aquela pessoa que recebe o seu pedido de hambúrguer e grita ele pra cozinha toda ouvir, e quase que automaticamente, alguém coloca batata pra fritar, pão para esquentar e hambúrguer pra grelhar.
Mensagens e eventos passam por um intermediário
Como você já deve ter percebido, não estamos mais dentro de uma aplicação monolítica. Estamos falando de serviços distribuídos. E com isso você pode entender SoA (Service-oriented Architecture) ou Micro serviços. Acredito até que a comunicação por messaging queue e event streaming são o ponto de convergência – e de confusão – entre SoA e Micro Serviços. Mas esse é papo para outro momento.
Como são aplicações que não compartilham a mesma heap e stack de memória (as vezes sequer estão no mesmo computador) se faz necessário que essas mensagens e eventos sejam intermediadas por algum middleware que seja capaz de entregar as mensagens corretamente. E embora pareça simples, entregar uma mensagem uma única vez para um único sistema não é um trabalho simples.
Esse middleaware também é chamado de message broker. Entre os mais conhecidos figura o RabbitMQ, que implementa o protocolo AMQP. Cito esse protocolo porque ele estabeleceu as bases do messaging queue. Caso você não queira estudar toda a especificação do protocolo, você pode consultar esta página do RabbitMq. Lá você vai encontrar explicações de alto-nível sobre como funciona esse protocolo – e como ele está implementado no RabbitMq.
Onde entra o Kafka nessa história?
O Apache Kafka nasceu no LinkedIn como um messaging queue. Com a sua abertura para a comunidade, tornou-se uma plataforma de event streaming capaz de entregar trilhões de eventos por dia! Essa eficiência e versatilidade fizeram do Kafka a opção de event streaming do Uber e de data streaming da Netflix. Ou seja, quando você está assistindo a sua série favorita, você está utilizando Kafka!
Além de ser rápido, o Kafka consegue ser altamente configurável. Com certa facilidade, você consegue fazê-lo rodar num cluster de servidores, garantindo (quando necessário) que as diversas instâncias recebam a mensagem e confirme o seu recebimento. Também é possível configurar, no cliente, algumas características que vão influenciar na latência e no throughput (vazão) das mensagens.
A forma como trabalha o recebimento, armazenamento e entrega das mensagens, permite que ele seja utilizado como messaging queue, servindo uma aplicação apenas, ou também como event broker, servindo eventos para vários consumidores simultâneos.
Como pode perceber, estou encantado com o Kafka!
Concluindo
Mais importante do que os conceitos que nós aprendemos hoje, eu espero ter sido claro em relação aos problemas que buscamos resolver com mensageria e eventos. Vimos que são conceitos distintos, mas ainda assim muito próximos. E que como tudo na nossa área, não são bala de prata. E como toda solução arquitetural, trás alguns outros problemas. Como com a distribuição de transações, por exemplo. Fazer o “rollback” de uma transação vai ficar bem mais difícil quando você tiver mais de uma aplicação envolvida na transação. Mas isso é tema para uma outra SAGA de artigos.
Nos próximos artigos, quero mergulhar mais profundamente no Kafka. Explicar alguns conceitos básicos do seu funcionamento. Não se sinta mal por não escrevermos código hoje. É melhor que esses conceitos fiquem bem fixos no seu entendimento para que possamos codificar com maior segurança.
Então, até lá!
Muito bom! Ótimos exemplos e bem explicado! Ta salvo…
Realizei uma excelente leitura e vou implementar em meus projetos.
Muito bom o artigo, muito bem explicado e ótimas analogias. Parabéns!