Quando um consumidor grava mensagens em um sistema de arquivos de rede (NFS) de forma síncrona no loop principal de poll, o processamento fica mais lento e pode bloquear totalmente o consumo.
Por que o NFS bloqueia o consumo
Os consumidores do Kafka usam um modelo baseado em pull: o consumidor chama poll() para buscar um lote de mensagens, processa-as e chama poll() novamente. Qualquer operação lenta no loop de poll — como uma gravação síncrona no NFS — atrasa a próxima chamada de poll() e reduz o throughput.
O NFS agrava esse problema de duas formas:
O NFS é mais lento que o armazenamento local. A latência de gravação em um sistema de arquivos de rede compartilhado supera a de um disco conectado localmente, o que aumenta diretamente o tempo de processamento por mensagem.
Vários consumidores competem pelos recursos do NFS. Embora o NFS permita acesso simultâneo de múltiplos consumidores, cada um disputa a mesma largura de banda de rede e E/S de disco. Quanto mais consumidores compartilham o NFS, pior é o desempenho individual.
Desacople o consumo do armazenamento
Duas abordagens resolvem esse problema. Use-as de forma independente ou combinada.
Separe o consumo e o armazenamento em duas threads (recomendado)
Desacople a busca de mensagens do armazenamento usando duas threads independentes:
Thread de consumo: Chama
poll(), processa as mensagens e insere os resultados em uma fila na memória (comoBlockingQueueem Java ouqueue.Queueem Python).Thread de armazenamento: Lê da fila na memória e grava os resultados no NFS.
Esse design impede que as gravações no NFS bloqueiem a thread de consumo, que continua buscando mensagens em velocidade máxima.
+-----------------------+ +----------------+ +-----------------------+
| Consumption thread | | In-memory | | Storage thread |
| |----->| queue |----->| |
| poll() + process | | BlockingQueue | | Write to NFS |
+-----------------------+ +----------------+ +-----------------------+
Monitore o tamanho da fila na memória. Se a thread de armazenamento não acompanhar o ritmo, a fila cresce indefinidamente. Defina uma capacidade máxima para a fila e estabeleça uma estratégia de backpressure, como bloquear a thread de consumo quando a fila estiver cheia.
Use discos locais em nuvem com sincronização assíncrona para o NFS
Anexe um ultra disk ou unidade de estado sólido (SSD) a cada consumidor e grave os resultados no armazenamento local em vez de gravar diretamente no NFS. Em seguida, use uma thread ou ferramenta assíncrona separada para sincronizar os dados dos discos locais em nuvem com o NFS em segundo plano.
Essa abordagem oferece dois benefícios:
Elimina a contenção do NFS durante o consumo. Cada consumidor grava em seu próprio disco local; portanto, deixam de competir pelos recursos compartilhados do NFS.
Impede que gravações síncronas no NFS bloqueiem o loop de poll. O processo de sincronização assíncrona roda de forma independente, então a latência do NFS não afeta o processamento de mensagens.