10 mins

Uma breve introdução sobre Apache Kafka

Bem vindo a mais um tutorial turma. Desta vez iremos rapidamente compreender a arquitetura e funcionamento do Apache Kafka.

1. Introdução ao Apache Kafka

O Apache Kafka é uma plataforma de streaming distribuída que é usada para construir pipelines de dados em tempo real e aplicativos de streaming. É um sistema de mensagens de alto desempenho e baixa latência que é projetado para lidar com grandes volumes de dados e permitir a troca de dados em tempo real entre aplicativos.

O Apache Kafka foi originalmente desenvolvido pelo LinkedIn e, posteriormente, foi doado para a Apache Software Foundation. Ele é escrito em Java e é uma plataforma de código aberto que pode ser executada em uma variedade de sistemas operacionais, incluindo Linux, macOS e Windows.

A plataforma é frequentemente usada em casos de uso como análise de dados em tempo real, processamento de stream, integração de sistemas, agregação de logs e métricas, entre outros.

O Kafka é baseado em uma arquitetura distribuída, o que significa que ele é capaz de escalar horizontalmente e lidar com grandes quantidades de dados em tempo real. É também altamente tolerante a falhas, com recursos de replicação e failover integrados.

Em resumo, o Apache Kafka é uma plataforma de streaming distribuída que oferece alta escalabilidade, baixa latência e tolerância a falhas para casos de uso de streaming de dados em tempo real.

2. Partes que compõem o Apache Kafka

O Apache Kafka é composto por várias partes que trabalham juntas para fornecer um pipeline de dados escalável e de baixa latência para processamento e transmissão de dados em tempo real. Essas partes incluem produtores, consumidores, tópicos, partições, brokers, ZooKeeper e grupos de consumidores.

kafka-architecture

fig. 1 - arquitetura do kafka

Cada uma dessas partes tem um papel importante no funcionamento do Kafka e na entrega dos dados de forma eficiente e confiável.

  • Producers: são responsáveis por enviar dados para o Kafka. Eles produzem os dados e os enviam para um ou mais tópicos do Kafka.

  • Consumers: são responsáveis por receber dados do Kafka. Eles se inscrevem em um ou mais tópicos e consomem os dados produzidos pelos produtores.

  • Consumers Groups: são grupos de consumidores que compartilham a carga de leitura de partições de um tópico. O objetivo é permitir que os consumidores possam se balancear e escalonar horizontalmente conforme necessário.

  • Topics: são fluxos de dados semelhantes a uma categoria ou um feed de notícias. Eles representam as categorias ou áreas de interesse dos dados no Kafka.

  • Partitions: são divisões lógicas de um tópico no Kafka. Elas são usadas para distribuir e paralelizar o processamento de dados em vários nós do Kafka Cluster.

  • Brokers: são os nós do Kafka Cluster que armazenam e distribuem as partições do tópico em diferentes nós do cluster. Cada nó do Kafka é um broker que executa uma ou mais instâncias do Kafka.

  • ZooKeeper(Opcional): é um serviço de coordenação distribuída que é usado para gerenciar e manter a configuração do Kafka Cluster. O ZooKeeper é responsável por armazenar informações de configuração, monitorar a disponibilidade dos nós do Kafka e coordenar o processo de failover.

No geral, todas essas partes trabalham juntas para criar um sistema distribuído de streaming de dados que pode lidar com grandes volumes de dados em tempo real, oferecendo alta escalabilidade, baixa latência e tolerância a falhas.

3. Tópicos e Partições no Apache Kafka

De forma geral, o Kafka armazena registros em tópicos. Portanto, quando enviamos um novo registro para o Kafka, devemos decidir o nome do tópico para onde ele deve ser armazenado. Também, devmos fornecer o nome de um tópico para receber e processar registros do Kafka.

Quando falmos de registros, eventos, mensagens estamos essencialmente falando da mesma informação. No decorrer deste post iremos chamar as informações enviadas e coletadas dos tópicos de mensagens.

Pense nos tópicos como a fila ou tabela em que você deseja armazenar seus dados. Gostei bastante da comparação entre tópicos e tabelas de banco de dados feita nesta apresentação de Robin Moffatt. Por exemplo, você tem um tópico que contém pedidos, o que é bastante razoável para comparar com uma tabela de banco de dados que contém informações de pedidos. Suas aplicações podem, então, usar este tópico para enviar informações sobre pedidos conforme eles chegam enquanto serviços downstream os processam.

Na terminologia do Kafka, os produtores enviam novas mensagens, enquanto os consumidores as lêem mais tarde (embora possivelmente quase instantaneamente). Os tópicos conectam produtores e consumidores criando uma ponte entre eles.

kafka-producer-topic-consumer

fig. 2 - fluxo de comunicação entre produtores, tópicos e consumidores

Um tópico é uma uma categoria para as mensagens que estão sendo transmitidas. Cada informação é associada a um tópico específico, que é definido por um nome. Quando um produtor envia uma mensagem, ele especifica o nome do tópico ao qual essa mensagem está relacionada.

Por sua vez, os consumidores podem se inscrever em um ou mais tópicos, de acordo com seus interesses. Isso significa que eles receberão apenas as mensagens que estão relacionadas aos tópicos nos quais se inscreveram. Por exemplo, na imagem a seguir percebemos que os consumidores se conectaram apenas ao tópico ORDERS.

kafka-producer-topic-consumer-detailed

fig. 3 - fluxo de comunicação entre produtores, tópicos e consumidores no detalhe

Embora as duas imagens anteriores descrevam bem o que são tópicos, elas fornecem apenas uma visão geral. Para compreender melhor o funcionamento de um tópico, é necessário investigar mais a fundo.

Um tópico é constituido por multiplas queues internas chamadas de partições, que armazenam as mensagens enviadas para o Kafka. Por exemplo:

kafka-topic-partitions

fig. 3 - organização de partições dentro do tópico

Cada partição em um tópico mantém sua própria ordem, o que é fundamental para a escalabilidade do Kafka. Os produtores podem enviar mensagens para qualquer partição em um tópico, escolhendo uma partição com base na chave da mensagem ou especificando explicitamente uma partição. Esse método de divisão de mensagens permite que cada partição cresça em ritmos semelhantes.

Ao se inscrever em um tópico, os consumidores são designados para receber mensagens de algumas das partições desse tópico.

Cada consumidor recebe mensagens de uma partição que mantém a ordem em que foram produzidos. Essa ordem possibilita o uso de vários consumidores para processar eventos de um único tópico, o que melhora o desempenho e a disponibilidade (desde que implementado corretamente).

kafka-topic-partitions-producers-consumers

fig. 4 - diagrama completo de comunicação entre produtores, tópicos e consumidores

4. Produtores e Consumidores no Apache Kafka

Os produtores são responsáveis por gerar o código do cliente Kafka, este por sua vez é utilizado para enviar as mensagens para os tópicos.

Esses clientes diminuem significativamente a complexidade de uso do Kafka, já que eles lidam com problemas como pooling de conexões e buffer de rede. Com isso, é possível interagir com o Kafka fazendo uso de uma pequena quantidade de APIs.

Em artigos futuros iremos utilizar o Java para implementar os produtores e consumidores.

O diagrama a seguir ilustra como um produtor envia uma mensagem para um broker, que, por sua vez, o registra em uma das partições dos tópicos.

kafka-producer-simple

fig. 5 - diagrama de comunicação entre produtor, broker e tópico

Os consumidores são responsáveis por gerar o código do cliente Kafka para ler as mensagens dos tópicos, atuando na extremidade oposta em relação aos produtores e aproveitando as mesmas funcionalidades, como pooling de conexões e buffer de rede.

O diagrama abaixo exemplifica a interação entre um consumidor e um broker, em que o consumidor recebe as mensagens armazenadas em um tópico.

kafka-consumer-broker-simple

fig. 6 - diagrama de comunicação entre consumidor, broker e tópico

5. Offset no Apache Kafka

Cada mensagem armazenada em uma partição tem a ela atribuída um offset que indica a distância dela em relação ao início da partição.

Adicionar novas mensagens aumenta o offset, garantindo que as mensagens mais recentes sempre terão um offset maior do que as mensagens mais antigos (com base no tempo em que foram armazenadas).

kafka-offset-in-partition

fig. 7 - atribuição de offset em uma partição

Os offsets não tão importantes ao se persistir novas mensagens, pois essa parte é toda tratada pelo Kafka. No entanto, o conceito desempenha um papel importante ao consumir as mensagens.

Os consumidores usam offsets das partições para acompanhar quais mensagens foram processadas. À medida que ele busca mensagens e realiza o processamento necessário, precisa de alguma formar marcar os offsets dessas mensagens, para que o consumidor saiba de qual ponto deve retomar a busca por mensagens em caso de falhas.

5.1 Commit dos offsets

O processo de acompanhamento dos offsets de um consumidor é conhecido como commit. Isso significa que o offset atual de uma partição é "commitado" e armazenado.

O consumidor não armazena seus offsets localmente. Em vez disso, ele os envia para o broker do Kafka, que os persiste em disco.

Ao manter os offsets remotamente no broker, reduzimos o acoplamento entre os clientes consumidores dentro do contexto da nossa aplicação. Desta forma é possível iniciar, parar e até mesmo destruir as máquinas que executam os consumidores sem impedir a recuperação da nossa aplicação.

É possível inicializar um novo cliente consumidor e continuar instantaneamente o processamento a partir do ponto em que o consumidor anterior parou.

6. Consumer Groups, agrupamento de consumidores

Os consumidores podem se agrupar em “grupos de consumidores”, que determinam quais mensagens cada um receberá. Embora isso possa parecer vago, não há outra maneira curta e sucinta de resumir este assunto.

Em resumo, os grupos de consumidores realizam o seguinte:

  • Agrupam os consumidores por função
  • Compartilham as partições de um tópico entre os consumidores do mesmo grupo

Os grupos de consumidoresnos permite relacionar um conjunto de consumidores que trabalhem juntos para executar uma única função, processo ou tarefa. Cada grupo de consumidores processa todas mensagens de um tópico independentemente de outros grupos.

Por exemplo, se você tiver um tópico que represente dados de vendas, um grupo poderá manter um agregado dos valores de venda enquanto outro passa para um sistema downstream para processamento posterior.

Ambos esses grupos serão executados independentemente e gerenciarão seus offsets, mesmo ao se inscreverem no mesmo tópico.

O diagrama abaixo ilustra dois grupos de consumidores que recebem todas as mensagens armazenadas no tópico:

kafka-consumer-groups-receive-all-records

fig. 8 - consumidores agrupados por função

6.1 Compartilhando as partições de um tópico entre os consumidores do mesmo grupo

Cada consumidor dentro de um grupo de consumidores é atribuído a um conjunto de partições de um tópico, garantindo o compartilhamento de mensagens entre o grupo e mantendo a ordem das mensagens em cada partição. Esse mecanismo é otimizado mantendo as atribuições de partições iguais até a adição de um novo consumidor ao grupo.

Nesse momento, o Kafka redistribui as partições entre os consumidores do grupo, um processo conhecido como “rebalanceamento”. Se houver mais partições do que consumidores em um grupo, os consumidores podem ser atribuídos a várias partições.

O diagrama ilustra o compartilhamento de partições em um único grupo de consumidores:

kafka-consumer-groups-multiple-consumers-in-group

fig. 9 - compartilhamento de partições entre os consumidores

A inclusão de outro grupo de consumidores não afeta a atribuição dos grupos existentes, como mostrado abaixo:

kafka-consumer-groups-multiple-consumers-in-multiple-groups

fig. 10 - novo grupo de consumidores não afeta a atribuição dos grupos existentes

Quando um novo consumidor é adicionado a um grupo, o Kafka realiza um rebalanceamento. Durante esse processo, as partições de cada tópico atribuídas ao grupo são redistribuídas entre todos os consumidores do grupo de maneira uniforme, garantindo que cada consumidor tenha aproximadamente o mesmo número de partições para consumir.

Esse processo ajuda a garantir o equilíbrio de carga entre os consumidores do grupo e otimiza o desempenho do Kafka.

kafka-consumer-groups-rebalance

fig. 11 - redistribuição das partições entre os consumidores do grupo

7. Executando um teste com o Apache Kafka utilizando o cli

Para executarmos um teste simples rodando um broker do kafka localmente precisaremos ter instalado o Java JDK 8 ou superior.

Realizae o download do Apache Kafka, no momento em que escrevo este artigo a versão mais recente é a 3.4.0 Scala 2.13.

Para demonstração iremos utilizar o sistema operacional Linux Ubuntu 22.04. Portanto para efetuar o download digite o comando abaixo:

$ wget https://downloads.apache.org/kafka/3.4.0/kafka_2.13-3.4.0.tgz

Depois de finalizar o download descompacte os arquivos e acesse a pasta kafka_2.13-3.4.0 com os comandos abaixo:

$ tar -xzf kafka_2.13-3.4.0.tgz
$ cd kafka_2.13-3.4.0

Agora inicialize o ZooKeeper e em seguida o Apache Kafka com os comandos abaixo:

$ ./bin/zookeeper-server-start.sh config/zookeeper.properties
$ ./bin/kafka-server-start.sh config/server.properties

zookeeper-kafka-broker-running

fig. 12 - zookeeper e apache kafka em execução

Vamos então criar o nosso tópico para que possamos publicar algumas mensagens e posteriormente consumir essas mensagens.

Para isso no terminal digite o comando abaixo:

$ ./bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic LIKES_POST

Para verificar se o tópico foi criado com sucesso, digite o comando abaixo:

$ ./bin/kafka-topics.sh --list --bootstrap-server localhost:9092

A saída será o nome do nosso tópico LIKES_POST.

Agora iniciamos o nosso produtor com o comando abaixo:

$ ./bin/kafka-console-producer.sh --broker-list localhost:9092 --topic LIKES_POST

Este comando libera a E/S para digitar as mensagens que poderão ser inseridas a cada linha, como abaixo:

>

Agora inicie o consumidor com o comando a seguir:

$ ./bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic LIKES_POST --from-beginning

Vamos então inputar algumas mensagens e acompanhar o consumidor.

producer-consumer-interaction

fig. 12 - interação entre produtor e consumidor

Conclusão

O Apache Kafka é uma plataforma distribuída de streaming que permite a troca de mensagens em tempo real entre aplicações de maneira escalável e confiável. Sua arquitetura distribuída garante alta disponibilidade, tolerância a falhas e escalabilidade horizontal.

Os tópicos do Kafka permitem organizar as mensagens por assunto, permitindo que as aplicações consumidoras obtenham as informações relevantes de acordo com sua necessidade.

Os produtores e consumidores são responsáveis por enviar e receber as mensagens para e do Kafka, respectivamente, e os offsets são usados pelos consumidores para acompanhar o progresso no processamento das mensagens.

O uso de grupos de consumidores permite agrupar consumidores com a mesma função, compartilhar as partições do tópico e garantir que cada consumidor processe uma parte exclusiva das mensagens.

Com esses conceitos em mente, é possível construir sistemas distribuídos altamente escaláveis e confiáveis usando o Apache Kafka como plataforma de streaming.


E é isso por hoje, pessoal!

Chegamos ao final de mais um artigo, espero que tenha sido útil e que você tenha aprendido algo novo.

Caso tenha alguma dúvida, comentário ou tenha encontrado algum erro, por favor, envie-me um email. Ficarei feliz em ouvir de você.

Se desejar receber novos artigos diretamente em seu e-mail, por favor, assine a nossa Newsletter. E se você já é um assinante, muito obrigado!

Aproveito e deixo um convite para nos conectarmos no Twitter e LinkedIn.

👋 Obrigado por ler até o final e até o próximo artigo !!!

Turbine a sua caixa de entrada

Junte-se a mais de 0 desenvolvedores inscritos

Talvez você possa curtir