Arquitetura

Escalabilidade horizontal de consumidores com Apache Kafka

Veja como entender os mecanismos internos do Apache Kafka, essa ferramenta poderosa e flexível no que diz respeito à escalabilidade.

Uma das vantagens das arquiteturas de microsserviços (MSA) é a possibilidade de escalar horizontalmente componentes isolados do sistema, de forma a evitar a degradação de performance – quando em alta demanda – e otimizar o uso dos recursos de infraestrutura – quando em janelas de pouca utilização.

Eventualmente, essas arquiteturas requerem alguma ferramenta que permita realizar a comunicação assíncrona entre microsserviços. Essas ferramentas, por sua vez, também precisam oferecer suporte para um aumento na quantidade de clientes e, evidentemente, de mensagens trocadas.

Uma das ferramentas da stack da HypeFlame é o Apache Kafka. Mas antes de entrar em detalhes sobre como escalar o número de consumidores, vamos a um breve resumo para refrescar alguns conceitos…

Anatomia dos Tópicos

Os tópicos são uma forma de classificar as mensagens de acordo com seu assunto, permitindo que apenas consumidores interessados recebam determinadas mensagens.

  • Partições: cada partição consiste em uma sequência imutável e ordenada de mensagens que são constantemente concatenadas ao commit log. O número de partições é definido a nível de tópico.
  • Offset: corresponde ao índice sequencial único da mensagem dentro da partição. 
  • Replication Factor: trata-se de uma configuração voltada à tolerância a falhas. O replication factor permite configurar o número de cópias de cada partição que serão mantidas de forma distribuída nos nós do cluster. Isso permite realizar o failover para as cópias secundárias em caso de queda de algum nó. Essa configuração também é definida a nível de tópico.

Grupos de consumidores

Já sabemos que os tópicos são uma forma de segmentar as mensagens pelo assunto a que se referem, e que esse recurso já permite que as mensagens sejam destinadas apenas aos consumidores interessados. Até aqui tudo bem… No entanto, o que fazer se um de seus consumidores não tiver capacidade de processar o volume de mensagens publicadas em um intervalo de tempo aceitável? Nesse caso vamos precisar escalar esse consumidor.

Quando escalamos nossos consumidores, caímos em um novo problema que é: como garantir que cada mensagem do tópico não seja lida por mais de um consumidor que fará exatamente a mesma coisa que as suas demais réplicas?

Para restringirmos a leitura da mensagem a apenas uma das réplicas de nosso serviço podemos utilizar consumer groups. 

Vejamos os seguintes exemplos:

Exemplo 1

2 microsserviços, 2 réplicas cada, 4 consumer groups distintos

No exemplo 1, cada réplica de cada microsserviço possui um grupo diferente. Dessa forma, uma cópia da mensagem será entregue para cada réplica do microsserviço, o que pode ser indesejado em alguns cenários.

Exemplo 2

2 microsserviços, 2 réplicas cada, 1 consumer group por microsserviço

No  Exemplo 2, cada microsserviço possui um grupo diferente, porém as réplicas de um mesmo microsserviço compartilham o mesmo grupo. Com isso, apenas uma das réplicas de cada serviço receberá uma cópia da mensagem. Com esse mecanismo, podemos escalar horizontalmente, evitando que a mesma mensagem seja processada N vezes por réplicas de um mesmo microsserviço.

Partições e número de consumidores no Apache Kafka

Ao conectar ou desconectar consumidores de um cluster de kafka, é realizado o rebalanceamento de partições para distribuir a carga. Em caso de sucesso no rebalanceamento, cada consumidor recebe zero ou mais partições.

Apesar de um mesmo consumidor poder receber mais de uma partição, a recíproca não é, necessariamente, verdadeira. Uma partição não pode ser atribuída a mais de um consumidor de um mesmo consumer group simultaneamente. Isso quer dizer que, mesmo que você escale infinitamente seu microsserviço, as mensagens serão distribuídas até o limite de N consumidores, onde N é o número de partições configurado para o seu tópico.

Exemplo 3

1 microsserviço, 5 réplicas, 3 partições

No Exemplo 3, apesar de termos cinco réplicas disponíveis do mesmo microsserviço, apenas três delas receberão mensagens do tópico, pois ele foi criado com apenas 3 partições. 
Se a demanda pelo consumo de mensagens  for alta e precisarmos escalar nossos consumidores, será necessário calibrar nossa quantidade de partições.

Não existe uma fórmula mágica para a quantidade de partições de um tópico, no entanto, um bom chute inicial pode ser manter a quantidade de partições próxima ao número médio de réplicas dos microsserviços consumidores. 

Apache Kafka na prática

Para demonstrar essas características, fizemos um breve exemplo utilizando os próprios scripts utilitários do Apache Kafka. Vamos tomar como base o Exemplo 3, descrito acima.

  1. Primeiramente, fizemos o download do Apache Kafka.
  2. Em uma nova janela do terminal, navegamos até a pasta onde foi baixado o Apache Kafka executamos o zookeeper-server:
bin/zookeeper-server-start.sh config/zookeeper.properties

3. Em uma nova janela do terminal, iniciamos um broker com o seguinte comando:

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

4. Em uma terceira janela do terminal, criamos um tópico com o seguinte comando:

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

5. O resultado foi o seguinte

6. Após isso, iniciamos 5 consumidores, um em cada janela vinculados ao mesmo consumer group, com o seguinte comando

bin/kafka-console-consumer.sh --topic three-partitions-topic --from-beginning --bootstrap-server localhost:9092 --group group-one

7. Por fim, publicamos algumas mensagens no tópico criado, em uma nova janela, com o seguinte comando

for i in $(seq 1 50); do printf "mensagem $i\n" | bin/kafka-console-producer.sh --topic three-partitions-topic --bootstrap-server localhost:9092; done

8. O resultado observado foi o seguinte:

  1. Conforme esperado, apenas 3 consumidores receberam as mensagens, o que significa que temos réplicas ociosas do serviço.
  2. Para aproveitar melhor nossos consumidores, precisaremos fazer Scale Up nas partições do nosso tópico

Scale UP de Tópicos

  1. Executamos o seguinte comando para aumentar a quantidade de partições para 5
bin/kafka-topics.sh --zookeeper localhost:2181 --alter --topic three-partitions-topic  --partitions 5

2 . Por fim, publicamos novas mensagens no tópico criado, em uma nova janela, com o seguinte comando

for i in $(seq 1 50); do printf "mensagem $i\n" | bin/kafka-console-producer.sh --topic three-partitions-topic --bootstrap-server localhost:9092; done

3. O resultado obtido foi o seguinte

  1. Conforme previsto, os 5 consumidores receberam mensagens do tópico three-partitions-topic

Atenção: o Apache Kafka não suporta a diminuição do número de partições, pois a remoção de uma partição significa perda de dados. Portanto, só devemos escalar a quantidade de tópicos quando realmente for necessário. Para reduzir a quantidade de partições, uma estratégia possível é a criação de um novo tópico, com menos partições que o anterior e o consequente apontamento dos consumidores para esse novo tópico.

Conclusão

O Apache Kafka é uma ferramenta poderosa e flexível no que diz respeito à escalabilidade. Como toda ferramenta, é preciso compreender seus mecanismos internos de tunning para evitar surpresas e gargalos de performance por configuração indevida ou não realizada. É importante sempre ter em mente que algumas configurações, como o aumento de partições de um tópico, não são muito triviais de se reverter. 

Referências

%d blogueiros gostam disto: