Imagem-destaque-min

INTRODUÇÃO AO APACHE KAFKA

Escrito por Jefferson Lima
 em 8 de julho de 2021

E aí galera, tudo bem? Sou Jefferson Lima, Engenheiro de Software na DBC, atuo há bastante tempo com desenvolvimento backend e ultimamente tenho trabalhado em uma arquitetura baseada em microsserviços. Nesse artigo eu vou apresentar o Apache Kafka, uma das ferramentas mais utilizadas atualmente para lidar com alguns dos desafios encontrados nesse, e diversos outros, casos de uso.

O Apache Kafka é uma plataforma distribuída de streaming de eventos, ele começou a ser desenvolvido no Linkedin em 2008 e teve o código aberto em 2011 quando se tornou parte da Apache Software Foundation. Desde então, passou a ser amplamente adotado por grandes empresas de diversos setores, incluindo algumas gigantes da área de tecnologia como Netflix, Twitter e Spotify.

Desde sua concepção, o Kafka foi projetado com alguns princípios chaves em mente, entre eles, fornece uma API simples para produtores e consumidores, garantir altos níveis de throughput e disponibilidade, bem como ter uma arquitetura escalável. Isso possibilitou que hoje ele possa processar uma grande quantidade de dados em um curto espaço de tempo, além de ser capaz de escalar para até milhares de servidores, com centenas de partições, que podem suportar trilhões de mensagens por dia.

A plataforma conta ainda com uma vasta documentação e uma ampla comunidade online, sendo atualmente um dos cinco projetos mais ativos da Apache Software Foundation.

Tudo isso faz com que ele seja ideal para uso em diversos tipos de aplicações, de diversos tamanhos, inclusive aplicações de missão crítica, que se aproveitam da tolerância à falhas e garantias de entrega de mensagens fornecidas pelo Kafka.

CASOS DE USO

O Kafka se tornou popular especialmente por seu uso como uma ferramenta de mensageria, mas sua versatilidade vai além disso e ele pode ser utilizado em diversos cenários, como nos exemplos abaixo:

  • Mensageria: Forma de comunicação assíncrona que desacopla as partes que se comunicam. Nesse modelo, uma parte envia os dados em forma de mensagem para o Kafka, para que sejam consumidos posteriormente por outra aplicação.
  • Rastreamento de atividades: Possibilita armazenar e processar dados de rastreamento da interação de um usuário com um site, como visualizações de páginas, cliques, entrada de dados, etc; geralmente esse tipo de atividade gera um grande volume de dados.
  • Métricas: Envolve a agregação de dados e estatísticas de diversas fontes com o objetivo de gerar um relatório centralizado.
  • Agregação de logs: Agrega e armazena de forma centralizada arquivos de logs originados em outros sistemas.
  • Processamento de streams: Processamento de pipelines de dados que consistem em múltiplos estágios, onde o dado bruto é consumido de tópicos e agregado, enriquecido ou transformado em outros tópicos.
  • Event Sourcing: é um estilo de design de aplicação no qual as mudanças são gravadas como uma sequência ordenada de registros.

Para dar suporte a essas funcionalidades, a plataforma provê essencialmente quatro APIs, uma API de streams que atua como um processador de streams que consome dados de um tópico, transforma-os e escreve em outro tópico; uma API de conectores, que possibilita conectar tópicos a sistemas existentes, como bancos de dados relacionais; e por fim, as APIs de produtores e consumidores, que permite que aplicações publiquem e consumam dados do Kafka, e serão o foco deste artigo.

EVENTOS

Tipicamente, estamos acostumados a modelar os dados de nossas aplicações como “coisas” que são persistidas em um banco de dados, elas costumam ser entidades do domínio da aplicação como clientes, produtos, pedidos, etc. Um registro de uma entidade em um banco de dados costuma ser único e pode ser excluído ou alterado ao longo do tempo. Assim, o estado da aplicação é representado pelo estado atual do banco de dados.

No Kafka, normalmente os dados são modelados como uma série de eventos que representam fatos que aconteceram, como “produto criado” ou “cliente alterado”. Eventos são imutáveis, ou seja, uma vez criados não podem ser excluídos ou alterados. Nesse modelo, o estado da aplicação é obtido através da computação de todos os eventos.

Figura 1 – Exemplos de eventos

No exemplo da Figura 1 temos dois eventos, um de cadastro de usuário e o outro de alteração de seu telefone. Para saber os dados atuais do usuário, computa-se todos seus eventos em sequência.

A modelagem baseada em eventos traz algumas vantagens como a possibilidade de os eventos serem consumidos e interpretados de maneiras diferentes por aplicações distintas, ou a capacidade de se recompor o estado da aplicação em qualquer ponto no tempo. Porém, essas vantagens vêm acompanhadas de alguns desafios, visto que, tipicamente esse é um modelo mais complexo e consequentemente, mais difícil de gerenciar.

Essa abordagem se adequa muito bem a casos de usos nos quais são produzidas streams de eventos, ou seja, um fluxo contínuo de deles. A origem destes dados podem ser dispositivos móveis, sensores, aplicações de software, etc. Alguns exemplos de streaming de eventos são transações financeiras em tempo real, monitoramento de veículos e captura de dados de sensores em ambientes industriais.

A representação de um evento no Kafka possui uma chave, valor, data e hora e cabeçalhos de metadados. O conteúdo da chave e do valor são representados como array de bytes e não possuem nenhuma estrutura ou significado especial para a plataforma, logo, o formato e o tamanho destes são determinados exclusivamente pela aplicação. Além disso, a chave não é necessariamente um identificador único de um evento, como uma chave primária em um banco de dados relacional. Dessa forma, é possível que múltiplos eventos possuam a mesma chave. Na verdade, veremos que esse é um ponto muito relevante no funcionamento do Kafka, e por isso a escolha da chave é uma decisão importante.

MODELO PUBLISH AND SUBSCRIBE

Figura 2 – Modelo publish and subscribe

O Kafka utiliza um modelo de produtores e consumidores, esta é uma forma de comunicação assíncrona na qual a parte que envia uma mensagem, o produtor, não se comunica diretamente com a parte que a recebe, o consumidor. Ao invés disso, a comunicação é intermediada por um outro componente, neste caso, o Kafka.


Figura 3 – Modelo requisição e resposta

Esse tipo de comunicação contrasta com o modelo típico de requisição e resposta, e traz vantagens como desacoplar as partes que se comunicam, permitir a comunicação assíncrona, possibilitar o buffer de mensagens e favorecer a escalabilidade.

No Kafka, os eventos são “empurrados” pelos produtores, ou seja, enviados destes para o Kafka, e “puxados” pelos consumidores, isto é, requisitados por eles. Isso evita que consumidores sejam sobrecarregados.

CLUSTER E BROKERS

Figura 4 – Cluster

O Kafka é um sistema distribuído, isto significa que ele é um sistema composto por múltiplos componentes que trabalham em conjunto, criando a impressão para o usuário final de que se trata de apenas um componente.

Em um cluster, que é um tipo específico de sistema distribuído, cada nó executa a mesma tarefa, no caso do Kafka, importar e exportar eventos, armazená-los e processá-los. Essa arquitetura é um dos principais fatores que garantem ao Kafka um alto nível de tolerância à falha e escalabilidade.

Cada nó em um cluster Kafka é chamado de broker e um cluster pode ter até centenas ou milhares de brokers. Um broker pode ser implantado em máquinas físicas, virtuais ou containers. Cada um deles tem conhecimento e se comunica com os demais, porém para se conectar ao cluster basta conhecer apenas um. Esse broker utilizado para se conectar ao cluster é chamado de bootstrap broker.

TÓPICOS

Figura 5 – Tópicos

Os eventos são agrupados em tópicos, que em muitos casos representam conceitos do domínio da aplicação, como “pedidos” ou “vendas”. Pode-se dizer então que, a grosso modo, um tópico está para o Kafka assim como uma tabela está para um banco de dados relacional.

Os eventos em um tópico são armazenados em uma estrutura de log imutável. Isso significa que os eventos são sempre adicionados ao fim do log, e nunca excluídos ou alterados.

PARTIÇÕES

Figura 6 – Partições

Os tópicos podem ser divididos em múltiplas partições e cada uma delas irá armazenar uma parte dos eventos. Essas partições podem ser distribuídas e replicadas em múltiplos brokers. Essa divisão possibilita o paralelismo no Kafka, pois a quantidade de partições determina o número de consumidores concorrentes suportados pelo tópico. Sendo assim, um número maior de partições favorece um throughput maior.

A ordem dos eventos em uma mesma partição é garantida, mas ao consumir de múltiplas partições, um consumidor não pode esperar que os eventos estejam na mesma ordem que foram produzidos.

Os eventos em uma mesma partição são indexados por um offset auto incremental. Portanto, para se localizar um evento, é preciso saber seu tópico, partição e offset.

É importante também garantir que cada partição caiba inteiramente em um servidor, dessa forma, se houver uma limitação de armazenamento, é preciso planejar o número de partições de acordo.

ALOCAÇÃO DE EVENTOS

Como visto, os tópicos são divididos em partições e cada uma delas armazena uma parte dos eventos. Estes são enviados pelos produtores, que não escolhem diretamente em qual partição os eventos serão armazenados, no entanto, eles podem definir a chave, e a alocação dos eventos nas partições é determinada pelo valor de suas chaves. Eventos com a mesma chave são armazenados na mesma partição. Se a chave não for informada, o Kafka irá selecionar a partição automaticamente usando um algoritmo round robin.

Figura 7 – Alocação de eventos

Na Figura 7 vemos um exemplo da alocação. Os eventos recebidos primeiro estão mais à direita na stream, inicialmente eles são distribuídos em sequência nas partições, mas ao encontrar um evento com uma chave que já existe em alguma partição no tópico, o Kafka irá alocá-lo nesta mesma partição, como ocorre com os eventos com chave C e A.

A escolha da chave depende do domínio da aplicação. Por exemplo, se for importante que os eventos de uma determinada entidade sejam consumidos em ordem, pode-se utilizar um um identificador desta para que todos os seus eventos sejam armazenados na mesma partição, e consequentemente ordenados.

CONSUMIDORES

Figura 8 – Consumidores

Tipicamente, todos os consumidores recebem eventos de todas as partições. Isso quer dizer que o mesmo evento pode ser consumido múltiplas vezes por consumidores diferentes, e que eles não são excluídos após serem consumidos. Diante disso, os consumidores precisam definir a partir de que ponto desejam consumir. Este ponto pode ser do primeiro evento, do último, ou qualquer outro entre esses dois e esse controle é feito através do offset. Isso traz grande flexibilidade no consumo de eventos, possibilitando por exemplo, que um consumidor retroaja e consuma novamente todos os eventos de um tópico.

Figura 9 – Grupo de consumidores

Um caso de uso muito comum é aquele em que diversos consumidores trabalham de forma concorrente, isto é, dividem o trabalho e cada um deles consomem um evento exclusivamente. No Kafka isso pode ser atingido agrupando esses consumidores. Dessa maneira, as partições de um tópico serão divididas para os consumidores de um grupo e cada um consumirá das partições que lhe foram atribuídas. Consequentemente, o número de consumidores concorrentes em um grupo é limitado pelo número de partições. Caso haja mais consumidores no grupo do que partições, os consumidores excedentes ficarão ociosos.

REPLICAÇÃO

Como visto, uma das vantagens do Kafka é a alta tolerância à falha, essa qualidade se deve a sua natureza distribuída que permite que os dados sejam replicados automaticamente em diferentes brokers. Com isso, mesmo que um deles falhe, é possível recuperar seus dados de algum outro.

Cada tópico tem um fator de replicação que determina quantas cópias de cada partição vão existir no cluster. Quanto maior o fator de replicação, maior será o nível de tolerância à falha, mas por outro lado, aumenta-se também o tempo de replicação e o espaço de armazenamento necessário.

Figura 10 – Replicação

Para que ocorra a replicação, é eleito um broker para ser o líder de cada partição, e os demais serão seus seguidores. Todas as leituras e escritas vão para o líder, e posteriormente são replicadas para os seguidores. Quando um líder “morre”, outro é eleito dentre os seguidores que estavam em um estado consistente com o ele.

Na Figura 10, temos três brokers e um tópico com fator de replicação igual a dois, o que significa que irá existir uma cópia de cada partição. Por isso, vemos que a partição P0 encontra-se nos brokers 1 e 3, a partição P1 nos brokers 1 e 2 e a partição P2 no 2 e 3.

ENTREGA DE MENSAGENS

Outro fator ligado à tolerância à falha é a garantia de entrega de mensagens. O Kafka permite que a estratégia de entrega seja configurada para atender diferentes casos de uso que possuem necessidades distintas em termos de confiabilidade de entrega e throughput.

Existem três modos de entrega de mensagens, em um deles é feita apenas uma tentativa, nesse caso os produtores enviam as mensagens de forma completamente assíncrona e não esperam pela confirmação de recebimento. Assim, caso o envio falhe a mensagem será perdida, porém ela nunca será produzida em duplicidade. Do lado do consumidor, este confirma o recebimento da mensagem assim que ela é consumida, mesmo que ainda não tenha processado ela. Caso o processamento falhe, o consumidor terá que lidar com o problema, descartando a mensagem ou solicitando o reenvio do mesmo offset novamente, mas o Kafka não irá fazer o reenvio automaticamente.

Outro modo permite que seja feita ao menos uma tentativa de entrega, neste o produtor irá fazer o envio e aguardar a confirmação de recebimento, caso esta não chegue, ele poderá tentar novamente. Sendo assim, caso o recebimento da confirmação falhe, ele poderá produzir mensagens duplicadas, mas por outro lado, nenhuma será perdida. Já o consumidor irá confirmar o consumo da mensagem apenas após processá-la, caso o processamento falhe o Kafka irá reenviar o mesmo offset.

Por fim, a plataforma consegue ainda garantir o envio de mensagens exatamente uma vez com garantia de recebimento quando se publica de um tópico para outro usando Kafka Streams.

COMPACTAÇÃO DE LOG

Já sabemos que os eventos não são excluídos automaticamente após serem consumidos. De fato, o Kafka pode ser configurado para armazená-los permanentemente ou descartá-los apenas após um período, ou ao atingirem um determinado tamanho.

O descarte de eventos acontece através de um processo chamado compactação de log, no qual será mantido pelo menos o último estado conhecido para cada chave dentro de uma partição. Com isso, é possível reduzir o tamanho do log sem que se perca o estado atual da aplicação.

Figura 11 – Compactação de log

Na Figura 11, vê-se à esquerda a partição antes da compactação à direita a partição compactada. É possível notar que após a compactação foram mantidos apenas os eventos mais recentes para cada chave.

QUOTAS

Clientes podem produzir ou consumir altos volumes de dados ou gerar requisições a uma taxa muito alta, isso pode acabar sobrecarregando o sistema e afetar sua responsividade. Para evitar essa situação, o Kafka permite configurar quotas que controlam os recursos dos brokers usados pelos clientes. Essas quotas podem ser baseadas na largura de banda de rede ou na taxa de requisições alocadas para esses clientes.

CONCLUSÃO

Neste artigo pudemos conhecer um pouco sobre parte da plataforma do Apache Kafka, vimos que ele é uma das ferramentas mais utilizadas atualmente para a publicação, armazenamento, transformação e consumo de fluxos contínuos de dados. Aprendemos que entre suas principais vantagens estão o alto throughput, escalabilidade e tolerância a falhas, além de termos visto os principais conceitos nos quais estão fundamentadas as APIs de produtores e consumidores. Por mais que ainda exista muito a se discutir sobre Kafka, essa introdução deve fornecer o conhecimento necessário para dominar diversos casos de uso.

Espero que tenham gostado!

Quer fazer parte do nosso time? #VemPraDBC

Confira nossas vagas em: https://dbc.compleo.com.br/

por Jefferson Lima Engenheiro de Software
Menu - DBC Company

Compartilhe

Compartilhar no facebook
Compartilhar no whatsapp
Compartilhar no twitter

Este post tem um comentário

  1. Alexandre Lunkes

    Melhor conteúdo sobre Kafka que li até agora. Muito bem explicado e me tirou muitas dúvidas sobre a arquitetura.

Deixe um comentário!

E participe da conversa.

Veja Também

Teste na arquitetura de Micros serviços
Hoje entramos no nosso quarto episódio de série de artigos mensais sobre teste de software e hoje vamos falar sobre o teste...
Scrum
É um processo leve para entregar valor para equipes multifuncionais e auto-organizadas.