Lorsque les partitions Kafka ne peuvent pas être augmentées, utilisez le multi-threading pour améliorer la capacité de consommation de Kafka (code source joint)

Il y a deux jours, csdn m'a rappelé que j'avais un autre fan, ce qui a stimulé ma motivation d'écriture. Ne cliquez pas pour voir le nombre de mes fans, haha!      

Dans des circonstances normales, les données des threads consommateurs de Kafka sont partitionnées (patition) un à un, et un seul patition est la plus petite unité d'opération parallèle de Kafka. Kafka ne permet que les données d'une seule partition soient consommées par un thread consommateur. Par exemple, nous faisons 20 partitions. En fait Correspondant à 20 fils consommateurs, lorsque nous faisons certaines activités, il y aura une forte augmentation du nombre de messages, et nos fils consommateurs sont limités, et la capacité de traiter les messages peut ne pas suivre, ce qui entraîne une grande quantité de messages qui ne peuvent pas être traités. .

Pour le moment, nous pouvons avoir besoin d'optimiser et d'augmenter la capacité de traitement. La plupart des gens pensent à ajouter des partitions. Les partitions peuvent être augmentées, mais il est impossible d'augmenter indéfiniment vers le haut. Nous utilisons ici une solution multithread.

package com.imcbb;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;

import java.util.concurrent.*;

/**
 * @author kevin
 * Date 2020-09-24
 * Time 09:43
 */
@Service
public class KafkaConsumer {
    private static Logger logger = LoggerFactory.getLogger(KafkaConsumer.class);


    ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 10, TimeUnit.SECONDS,
            new SynchronousQueue<>(),
            new ThreadFactoryBuilder().setNameFormat("KThread-%d").build(),
            (r, executor) -> {
                logger.warn("Ops,Rejected!");
                try {
                    executor.getQueue().put(r);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
//                        new ThreadPoolExecutor.CallerRunsPolicy()

    );

    @KafkaListener(topics = "myTopic")
    public void listen(ConsumerRecord<?, ?> cr) {

        executor.execute(() -> {
            logger.info("---------" + cr.toString());
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

    }
}

Dans le code ci-dessus, nous avons créé un pool de threads pour consommer les messages d'une partition. Voici deux points qui nécessitent une attention particulière:

1. La file d'attente pour stocker les tâches dans le pool de threads. Ici, nous utilisons la file d'attente de blocage SynchronousQueue. Cette file d'attente est très intéressante. Il s'agit d'une file d'attente sans capacité. Si vous êtes intéressé, veuillez vérifier Internet et ne donnez pas trop d'explications. SynchronousQueue est utilisé parce que, si Pour éviter la perte de messages accumulés dans la file d'attente lorsque le système est anormalement arrêté ou que l'application est redémarrée (bien sûr, cela ne peut pas empêcher complètement le thread de se bloquer en raison d'une exception dans le système pendant le travail des threads. Si une disponibilité plus élevée est requise, envisagez de le stocker d'abord dans la base de données. Dans redis, effectuez le mécanisme de traitement de la compensation).

2. Lorsqu'il y a trop de messages et que le pool de threads est trop occupé, nous avons deux solutions ici

2.1 Bloquez le thread consommateur d'écoute et utilisez le nouveau ThreadPoolExecutor.CallerRunsPolicy (), à ce moment le message ne sera plus consommé

2.1 Personnaliser la stratégie de rejet et remettre la tâche en main

(r, executor) -> {
    logger.warn("Ops,Rejected!");
    try {
        executor.getQueue().put(r);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

Les deux solutions ci-dessus peuvent résoudre le problème, il vous suffit d'en choisir une, aucune des deux solutions ne garantit l'ordre de traitement des messages.

Un avantage de l'utilisation du deuxième type est que vous pouvez vérifier la capacité de consommation du consommateur via le journal et voir s'il y a un rejet, pour ajuster le nombre de threads de manière appropriée.

Ce qui précède est la méthode de consommation multi-thread, qui a été testée dans notre environnement de production, haha!

L'article a simplement publié du code et écrit une démo complète pour tout le monde, vous pouvez le télécharger pour référence: https://github.com/kevinmails/kafka-consumer-demo

La condition préalable est d'installer kafka sur cette machine. Le manuel d'installation officiel (la plus petite version disponible, les tests locaux suffisent), si vous ne pouvez pas comprendre le message officiel, laissez-moi un message et j'écrirai un autre article d'installation. Il existe un manuel d'installation officiel: https://kafka.apache.org/quickstart

J'espère que tout le monde doit aider!

Référence: https://www.confluent.io/blog/how-choose-number-topics-partitions-kafka-cluster/

 

 

 

Je suppose que tu aimes

Origine blog.csdn.net/kevin_mails/article/details/108750743
conseillé
Classement