DEV Community

Marcelo Avancini
Marcelo Avancini

Posted on

Queues for Kafka: o que realmente resolve?

O Kafka é conhecido por sua escalabilidade e alto throughput. Como parte de seu marketing, destaca-se que ele é utilizado por mais de 80% das empresas da Fortune 100.

Em seu paper original, os desenvolvedores do Kafka deixam claro que incorporaram conceitos de agregação de logs e sistemas de mensageria, mas adotando decisões de design não convencionais. Justamente por não ser um sistema de mensageria convencional, é comum encontrar discussões sobre seus casos de uso e as razões pelas quais ele pode não ser ideal para certos cenários. O caso talvez mais polêmico seja o de comunicação entre microsserviços e criação de jobs, que muitas vezes se beneficiam de sistemas baseados em filas por conta dos recursos e comportamentos presentes nesse tipo de sistema. Padrões como requeue, per-message ack, DLQ e request-response são nativamente suportados por sistemas de filas tradicionais, enquanto no Kafka exigem abordagens alternativas.

Em contra partida, é comum que, após a adoção do Kafka para um caso específico, seu uso se expanda para diversos outros cenários. Isso já foi mencionado em um texto de 2015 por Jay Kreps, com base em sua experiência na Confluent, onde ele destaca aplicações que requerem larga escala como um dos principais casos de uso para a adoção do Kafka. No entanto, devido à sua natureza event-driven, novos consumidores podem ser adicionados facilmente, tornando sua adoção cada vez mais ampla e com novos casos de uso.

Soluções alternativas

Para emular alguns dos padrões e comportamentos citados, wrappers no lado do client são frequentemente desenvolvidos. Um exemplo é o artigo da Uber, que aborda estratégias para lidar com DLQs e reprocessamento. Entre as soluções open source, a própria Confluent mantém o Parallel Consumer, um wrapper que implementa filas no lado do client e suporta ACK per message.

Eu mesmo estive envolvido em projetos relacionados, implementando mecanismos de DLQ (a.k.a DLT pensando em Kafka), retry e processamento paralelo multi-threaded. Este último desacopla o consumo do processamento, permitindo a execução paralela por partição, conforme abaixo.

Image description

O princípio, embora simples, torna o wrapper responsável por controlar o commit, lidar com rebalanceamento, tratamento de erros, entre outros aspectos críticos. O diagrama acima vem deste artigo da Confluent, que descreve e implementa esse padrão. É possível perceber os diversos pontos que necessitam atenção.

A introdução do rebalanceamento cooperativo e incremental melhorou significativamente esse tipo de implementação, pois reduz o efeito stop the world e minimiza o impacto no processamento paralelo, especialmente ao garantir a entrega da mensagem pelo menos uma vez (at-least-once delivery), embora que não introduza nenhuma dos comportamentos atualmente emulados.

KIP-932 e filas "Kafka way"

As motivações expressas no KIP-932 ilustram bem os pontos discutidos até aqui. Embora o título faça referência a "Queues for Kafka", o foco real é habilitar comportamentos esperados em sistemas de filas, mas mantendo o "Kafka way", como destacado no próprio documento. Vamos entender melhor o que isso significa.

A principal mudança ocorre no funcionamento dos grupos de consumo, com a introdução de um novo tipo chamado "share". Esse tipo de grupo permite que múltiplos consumidores atuem sobre a mesma partição. Quando uma mensagem é recebida, ela fica indisponível para outros consumidores do grupo até que o ack (ou reject) da mensagem seja recebido ou o TTL expire. Sim, comportamentos típicos de sistemas de filas tradicionais.

No entanto, como mencionado, o "Kafka way" traz algumas particularidades. Atualmente, o protocolo de consumer groups delega grande parte da implementação aos clients. Com a proposta do KIP-848, parte dessa lógica será movida para o servidor (server-side), tornando-se um pré-requisito para a implementação dos share groups.

Por fim, vale dizer que a versão 4 do Kafka irá trazer uma versão simplificada da implementação, não pronta pra aplicarmos produção ainda, mas habilitando um primeiro teste. Isso também deixa aberto mudanças na implementação ou de algum comportamento até a liberação completa desse novo recurso.

Conclusão

O Kafka evoluiu desde seus principios descritos em seu famoso paper. No entanto, sua adoção para casos de uso que exigem padrões típicos de filas muitas vezes requer adaptações personalizadas no lado do client. Essas soluções, embora funcionais, adicionam complexidade e exigem um entendimento profundo do funcionamento interno do Kafka para evitar problemas como rebalanceamento inesperado e perda de controle sobre o processamento das mensagens.

Com propostas como o KIP-932 e o KIP-848, o Kafka busca incorporar funcionalidades mais próximas das filas tradicionais sem comprometer seus princípios arquiteturais. Apesar dessas melhorias, a escolha entre Kafka e sistemas tradicionais de filas ainda deve considerar os requisitos específicos da aplicação, garantindo que a tecnologia adotada esteja alinhada com os desafios e necessidades do sistema.

O gatilho pra criar o artigo veio dessa postagem no Linkedin: https://www.linkedin.com/posts/stephane-derosiaux_apachekafka-consumers-partitions-activity-7299741102171967489-Eomu/

Top comments (0)