Rafael Coelho

AllowMe

Em uma realidade onde o micro-serviço é a palavra de ordem, acabamos com tantos componentes de variados tipos e funcionalidades espalhados pelos ambientes que nosso novo e principal desafio passou a ser a gestão destes componentes.

O padrão bastante conhecido e usado atualmente  para fazer a comunicação entre os micro-serviços é o REST, só que as chamadas HTTP podem  ser custosas, e por padrão, sempre se espera uma resposta do serviço consumidor (mesmo que seja apenas um HTTP 200). Para resolver isto é, em geral, adotada uma arquitetura de mensageria para que os componentes possam ser tornar independentes entre si e também 100% assíncronos (aqueles que não precisam esperar nenhum tipo de resposta imediata).

É nesse cenário que entra o Apache Kafka: uma solução assíncrona, tolerante a falhas e escalável. A ferramenta foi desenvolvida inicialmente pelo LinkedIn (tendo na equipe Jay Kreps, Jun Rao, e Neha Narkhede) com o objetivo de solucionar problemas como a ingestão de baixa latência de grandes quantidades de dados de eventos do site e de sua infraestrutura, em uma arquitetura que utiliza o Hadoop e os sistemas de processamento de eventos em tempo real.

O Kafka é descrito como uma plataforma open-source de fluxo de dados (stream) distribuída, desenvolvida em Java e Scala e mantida pela Apache Foundation. O Kafka é capaz de armazenar os fluxos de mensagens de maneira durável e tolerante à falhas. Além disso, as mensagens que são trafegadas no Kafka não se perdem depois de serem consumidas.

 

Confluent.io, 2019.

Por que Utilizar o Apache Kafka?

Primeiro, deve-se levar em consideração o por quê de se usar uma mensageria de dados. Em geral, o objetivo de uma arquitetura MOM (Middleware Orientado a Mensagens) é permitir que o sistema e seus componentes trabalhem separadamente de forma assíncrona (e até de forma reativa em alguns casos) ganhando resiliência, de forma que eles possam trabalhar de maneira independente.

MOM e Mensageria são uma abordagem muito comum em micro-serviços, devido ao requisito de resiliência e assincronicidade.

E uma das características principais do Kafka é justamente sua resiliência. Diferente da maioria dos seus concorrentes, o Kafka é arquitetado para gravar mensagens, assim como redirecionar as que não tenham sido processadas em casos onde os consumidores parem ou retornem erros durante a execução.Sendo assim, o Kafka garante que nenhuma mensagem seja perdida por falha dos consumidores.

Outra característica importante é que ele é escalável horizontalmente, podendo, por exemplo, adicionar mais nós em horários de pico para rebalancear as partições, e assim, ter mais poder de armazenamento e processamento de  mensagens.

Naturalmente o Kafka é capaz de lidar com uma quantidade enorme de mensagens sem perder performance. Tendo seus brokers, tópicos e partições configuradas corretamente, o Kafka pode ser escalável e resiliente. 

O Apache Kafka em detalhes:

O Kafka contém diversas funcionalidades, das quais algumas são embarcadas e outras desenvolvidas pela Confluent.io. A seguir, oferecemos uma breve descrição sobre as suas principais funcionalidades:

  • Kafka Publish-Subscribe: É provavelmente a forma mais utilizada do Kafka, onde o Producer faz um “Publish” da mensagem e os consumers fazem um “Subscribe” a fim de receber as mensagens.

No Pub-Sub, existe o conceito de commit, onde a mensagem só é considerada lida por um consumer quando esse avisa ao Kafka que aquele lote foi processado, recebendo, em seguida, um novo lote de mensagens para processar.

  • Kafka Streams:  É um framework para tratar, transformar e agregar as mensagens. Funciona na mesma instância do cluster e broker que o pub-sub, mas processa uma mensagem por vez. Curiosidade: o nome vem do próprio streams do java e não do fato de ser um stream de mensagens (já que o pub-sub também seria um).

Em resumo, o Kafka Streams representa uma sequência de eventos que são imutáveis e não podem ser apagados.

  • Kafka Tables: Também faz parte do Framework Kafka Streams e é utilizado para representar o último estado de uma sequência de eventos. Ou seja, numa conversa hipotética, as palavras trocadas seriam os eventos ou streams, e o local onde é armazenada a contagem destas palavras seria a Table.
  • Kafka Connect: Esta ferramenta é desenvolvida pela Confluent.io para auxiliar na transmissão de dados entre o Kafka e outras aplicações e/ou sistemas de dados como, por exemplo, capturar todas as alterações feitas em uma tabela de um banco relacional.
  • KSQL: Também desenvolvida pela Confluent.io, basicamente fornece uma linguagem de query SQL para ler dados de tópicos do Kafka Tables.

Confluent.io, 2019

Confluent REST proxy: Mais uma ferramenta da Confluent.io, que fornece uma interface RESTful para um cluster Kafka, facilitando a produção e o consumo de mensagens, a exibição do estado do cluster e a execução de ações administrativas sem o uso do protocolo ou clientes Kafka nativos.

Como o Apache Kafka funciona:

Don’t fear the file system: Ao falar sobre o funcionamento do Apache Kafka, devemos mencionar a expressão “don’t fear the file system”, que em tradução livre seria “não tenha medo do sistema de armazenamento”. Os desenvolvedores do Kafka defendem que, se usado da forma correta, um HD padrão pode ter acesso mais rápido que a memória RAM. Isto é mencionado na documentação oficial, que está no item “4.2 Persistence”.

É verdade! Nenhuma mensagem no Kafka é gravada em memória. Todas elas são gravadas em disco assim que o Kafka as recebe do Publisher e, ao entregar a mensagem para o Subscriber, ela é lida novamente. Estas mensagens só serão apagadas quando se expira o tempo retenção (que por default é de sete dias), tornando possível recuperar uma mensagem que foi processada há dias.

A explicação dos criadores do Kafka é que a memória é mais rápida que o disco nos cenários de Random Access Memory, onde ela pode entrar e devolver uma informação posicionada em qualquer lugar, na mesma velocidade. Neste cenário, o disco apresenta o problema da movimentação física do braço e cabeça de leitura/gravação.

O que ocorre no Kafka é que basicamente as mensagens são gravadas de forma sequencial no HD, para que essa movimentação seja a mínima possível, tornando a busca da informação mais rápida. Além disso, o fato de não manter as mensagens em memória poupa o software de fazer a administração desta memória, como execução em demasia do garbage collectors, por exemplo.

Arquitetura do Kafka:

O Kafka tem uma arquitetura distribuída composta basicamente por brokers, tópicos e partições. Para orquestrá-los, é utilizado o Apache ZooKeeper (um serviço centralizado para manter informações de configuração, nomear, fornecer sincronização distribuída e serviços de grupo).

Conforme observa-se na imagem acima, um cluster é dividido em vários brokers, onde cada um deles pode ser uma máquina com uma instância do Kafka. Um único cluster atende a um datacenter, sendo possível configurar vários datacenters replicando mensagens entre si, aumentando ainda mais a escalabilidade e resiliência da solução.

Quando um producer ou consumer se conectar em um broker, o Kafka irá automaticamente se conectar aos outros brokers de forma discreta. Em resumo: sempre existirá um broker principal chamado bootstrap broker, porém, qualquer broker pode ser um bootstrap.

Na imagem acima também pode se notar que dentro do broker existem partições. Estas partições fazem parte do tópico, que são basicamente uma fila ou stream de dados. Em um cluster do Kafka podem existir quantos tópicos desejarmos.

OBS: É possível fazer uma analogia do tópico a uma tabela do banco de dados.

Um tópico é identificado por um nome e dividido em partições. Já uma partição é uma parte do tópico que contém as mensagens ordenadas, e cada mensagem recebe um número a partir de “0”, os chamados offset

 Apache, 2019

O Fator de Replicação é uma configuração do tópico para definir em quantos brokers o tópico em questão será replicado. Para cada partição de um tópico existe um broker que é o líder daquela partição e somente ele irá enviar ou receber as suas respectivas mensagens. As réplicas da partição em outros brokers manterão apenas uma cópia. Então, caso um broker seja corrompido (ou parar de responder por qualquer razão que seja), outro irá assumir seu lugar e será o líder daquela partição.

Stephane Maarek, 2019.

Ao criar um tópico, se define a quantidade de partições e a replicação. Não é possível existir mais replicações do que a quantidade de brokers, por isso, sempre existirá apenas uma instância da partição em cada broker

Cada partição atende apenas um consumer de cada grupo, mas um consumer pode receber mensagens de várias partições. O recomendado pela Apache é que cada consumer receba mensagens de pelo menos três partições.

A partir do momento que uma mensagem é gravada na partição, ela se torna imutável (não pode ser apagada, nem alterada), e só será expurgada quando atingir o tempo de retenção configurado. A mensagem é atribuída a uma partição de forma randômica, no caso de não ser atribuída uma chave específica (chamada key pelo Kafka). 

O consumer é o componente que consome a mensagem, e sempre estará atrelado a um grupo. Já os consumers de cada grupo consomem as mensagem de um tópico paralelamente, uma vez que o Kafka irá dividi-las entre esses consumers.

Os offsets organizam as mensagens dentro de uma partição e não tem significado para outra, cada uma tem um offset de leitura por grupo de consumer. O Kafka mantém o último offset lido e comitado pela aplicação.

Apache, 2019

Neste artigo fiz uma breve introdução aos conceitos e relevância do Apache Kafka em arquiteturas baseadas nos conceitos de micro-serviços. Como é um assunto de extrema relevância para entrega de soluções resilientes e de larga escala, vou continuar em uma próxima publicação aprofundando no assunto e trazendo exemplos reais de como usar o Kafka em cenários reais.

E você? Já usa o Kafka? Como faz a orquestração dos seus micro-serviços? Quais desafios têm encontrado na operação e uso do Kafka? 

Compartilhe em suas redes sociais!