Problèmes et solutions lors de l'utilisation du producteur Kafka en Node.js
Le choix d'une partition pour un message est une étape cruciale qui affecte la répartition de la charge parmi les consommateurs. Une logique de partitionnement incorrecte peut entraîner un déséquilibre et la perte de scalabilité.
Une erreur courante consiste à utiliser une fonction de partitionnement basée sur la division entière de la clé du message (key). Si la clé n'est pas un nombre ou ne peut pas être correctement convertie en entier (par exemple, un ObjectId MongoDB), tous les messages seront envoyés dans une seule partition.
const producer = await kafka.producer(topic, {
createPartitioner: function () {
return ({ partitionMetadata, message }: PartitionerArgs) => {
return partitionMetadata.length
? partitionMetadata[Number(message.key) % partitionMetadata.length]?.partitionId || 0
: 0;
};
},
});
Dans ce cas, les consommateurs du groupe ne pourront pas répartir efficacement la charge, car toutes les données sont concentrées dans une seule partition.
Solution :
- Valider le type et le format de la clé de partition au niveau du producteur.
- Mettre en place des métriques pour surveiller les déséquilibres de taille de partition.
- Utiliser des algorithmes de partitionnement plus fiables, tels que le hachage des clés chaînes.
Publication vers des partitions inexistantes
Les numéros de partition dans un sujet commencent à 0. Si le producteur tente d'envoyer un message à un index de partition supérieur au nombre disponible (par exemple, la partition 1 dans un sujet avec une seule partition), KafkaJS ne lance pas d'erreur. La méthode send() retourne un tableau vide, et le message est effectivement non écrit.
const result = await producer.send({
topic: 'user_events',
messages: [
{ key: '2', value: 'user_created', partition: 1 }
]
});
console.log(result); // []
Solution :
- Créer les sujets et partitions via des fichiers de configuration lors du déploiement pour éviter les erreurs manuelles.
- Valider la réponse de
send()au niveau de l'application. Si le résultat est vide ou contient une erreur (par exemple,errorCodeest différent de 0), prendre des mesures : lever une exception, journaliser, envoyer vers un sujet de secours ou envoyer une métrique. - Vérifier régulièrement les métadonnées du sujet (liste des partitions disponibles) avant d'envoyer.
Utilisation inefficace du producteur
Créer une nouvelle instance de producteur (kafka.producer()) pour chaque requête ou opération est une erreur fréquente entraînant un surcroît important de coûts. Chaque nouveau producteur établit des connexions réseau lourdes avec les brokers Kafka.
app.post('/send-message', async (request) => {
const { topic, message } = request.body;
const producer = kafka.producer(); // Nouvelle instance par requête
// ...
});
Solution :
- Utiliser le patron Singleton ou un pool de producteurs. Créer une seule instance de producteur et la réutiliser pour toutes les opérations d'envoi (
producer.send()). Cela améliore le débit et réduit la charge sur les brokers. - Pour les architectures multi-threadées ou microservices, envisager un client Kafka partagé avec un producteur partagé.
Problèmes de réseau et stratégie de réessai
La connexion réseau entre le producteur et le cluster Kafka peut être instable. Pour améliorer la fiabilité de la livraison des messages :
- Configurer la gestion de l'événement
request_timeoutpour suivre les problèmes réseau.
producer.on('producer.network.request_timeout', (error) => {
console.error({
code: 'producer_network_timeout',
message: error.message,
data: { stack: error.stack },
});
})
- Configurer des mécanismes de réessai (
retry) lors de la création du producteur. Par défaut, les réessais sont fixés à 5.
kafka.producer({
retry: { retries: 10 }
});
Important : Augmenter le nombre de tentatives sans stratégie appropriée (par exemple, sans augmenter les intervalles entre les essais) peut provoquer une charge similaire à un DDoS sur les brokers. Utilisez des configurations avec intervalles croissants (retard exponentiel).
Gestion des messages volumineux
Kafka n'est pas conçu pour gérer des messages de plus de 1 Mo. Envoyer des données importantes (5–10 Mo) entraîne des problèmes sérieux :
- Rotation fréquente des segments : Avec une taille maximale de segment faible (par exemple, 512 Mo), les messages volumineux déclenchent une rotation rapide, augmentant la charge système.
- Trafic réseau élevé : La réplication des messages volumineux sur tous les In-Sync Replicas (ISR) augmente la charge réseau.
- Débit réduit : Le tampon de messages du producteur grandit, augmentant les risques de perte de données et les délais d'attente pour les accusés de réception lorsque
acks=-1. - Consommation de ressources : Plus de RAM est nécessaire pour le cache de pages du système d'exploitation sur les brokers et pour le JVM Kafka, augmentant la charge CPU due à la collecte de déchets (GC).
Recommandations de taille de message :
- Utiliser la formule :
message.max.bytes = taille moyenne du message * 2, mais inférieure à 1 Mo. - Pour les grands fichiers (vidéo, audio, documents), utiliser un stockage externe (par exemple, S3). Envoyer uniquement une référence à l'objet dans le message Kafka.
- Si des JSON/XML volumineux doivent être envoyés sans stockage externe, le message contient probablement des données redondantes pour différents consommateurs. Considérer la séparation du sujet en plusieurs sujets spécialisés, chacun avec les champs nécessaires minimales.
- Une alternative, mais plus complexe : découper les messages volumineux en tronçons et les reconstituer côté consommateur.
Points clés
- Partitionnement : Un calcul incorrect de partition entraîne un déséquilibre de charge et la perte de scalabilité. La validation de la clé et la surveillance des métriques sont obligatoires.
- Existence de partition : Envoyer vers une partition inexistante ne déclenche pas d'erreur dans KafkaJS, mais le message n'est pas écrit. Valider la réponse de
send()est crucial. - Efficacité du producteur : Créer un nouveau producteur par requête est coûteux. Utiliser le patron Singleton ou un pool.
- Réseau et réessais : Configurer la gestion des timeouts réseau et des stratégies de réessai améliore la fiabilité de la livraison.
- Taille des messages : Envoyer des données supérieures à 1 Mo nuit à la performance du cluster. Utiliser un stockage externe pour les grands fichiers et optimiser la structure des messages.
— Editorial Team
Aucun commentaire pour le moment.