web-dev-qa-db-fra.com

Kafkaconsumer n'est pas sans danger pour l'accès multi-thread

J'utilise le code ci-dessous pour lire le sujet de Kafka et traiter les données.

JavaDStream<Row> transformedMessages = messages.flatMap(record -> processData(record))
                .transform(new Function<JavaRDD<Row>, JavaRDD<Row>>() {
                    //JavaRDD<Row> records = ss.emptyDataFrame().toJavaRDD();
                    StructType schema = DataTypes.createStructType(fields);

                    public JavaRDD<Row> call(JavaRDD<Row> rdd) throws Exception {
                        records = rdd.union(records);
                        return rdd;
                    }
        });

       transformedMessages.foreachRDD(record -> {
            //System.out.println("Aman" +record.count());
            StructType schema = DataTypes.createStructType(fields);

            Dataset ds = ss.createDataFrame(records, schema);
            ds.createOrReplaceTempView("trades");
            System.out.println(ds.count());
            ds.show();

        });

En exécutant le code, je reçois une exception ci-dessous:

Caused by: Java.util.ConcurrentModificationException: KafkaConsumer is not safe for multi-threaded access
    at org.Apache.kafka.clients.consumer.KafkaConsumer.acquire(KafkaConsumer.Java:1624)
    at org.Apache.kafka.clients.consumer.KafkaConsumer.seek(KafkaConsumer.Java:1197)
    at org.Apache.spark.streaming.kafka010.CachedKafkaConsumer.seek(CachedKafkaConsumer.scala:95)
    at org.Apache.spark.streaming.kafka010.CachedKafkaConsumer.get(CachedKafkaConsumer.scala:69)
    at org.Apache.spark.streaming.kafka010.KafkaRDD$KafkaRDDIterator.next(KafkaRDD.scala:228)
    at org.Apache.spark.streaming.kafka010.KafkaRDD$KafkaRDDIterator.next(KafkaRDD.scala:194)
    at scala.collection.Iterator$$anon$12.nextCur(Iterator.scala:434)
    at scala.collection.Iterator$$anon$12.hasNext(Iterator.scala:440)
    at scala.collection.Iterator$$anon$11.hasNext(Iterator.scala:408)
    at scala.collection.Iterator$$anon$11.hasNext(Iterator.scala:408)
    at org.Apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIterator.agg_doAggregateWithoutKey$(Unknown Source)
    at org.Apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIterator.processNext(Unknown Source)
    at org.Apache.spark.sql.execution.BufferedRowIterator.hasNext(BufferedRowIterator.Java:43)
    at org.Apache.spark.sql.execution.WholeStageCodegenExec$$anonfun$8$$anon$1.hasNext(WholeStageCodegenExec.scala:377)
    at scala.collection.Iterator$$anon$11.hasNext(Iterator.scala:408)
    at org.Apache.spark.shuffle.sort.BypassMergeSortShuffleWriter.write(BypassMergeSortShuffleWriter.Java:126)
    at org.Apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:96)
    at org.Apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:53)
    at org.Apache.spark.scheduler.Task.run(Task.scala:99)
    at org.Apache.spark.executor.Executor$TaskRunner.run(Executor.scala:322)

Le fait que je n’ai qu’un seul DStream, je ne sais pas trop pourquoi je reçois cette exception… .. Je lis à partir de 3 partitions d’un sujet Kafka. Je suppose que le "createDirectStream" va créer 3 consommateurs pour lire les données.

Vous trouverez ci-dessous le code correspondant à la méthode KafkaConsumer:

 private void acquire() {
        this.ensureNotClosed();
        long threadId = Thread.currentThread().getId();
        if(threadId != this.currentThread.get() && !this.currentThread.compareAndSet(-1L, threadId)) {
            throw new ConcurrentModificationException("KafkaConsumer is not safe for multi-threaded access");
        } else {
            this.refcount.incrementAndGet();
        }
    }
9
Amanpreet Khurana

Spark 2.2.0 propose une solution de contournement sans cache . Utilisez simplement spark.streaming.kafka.consumer.cache.enabled à false. Consultez la demande pull

6
Ehud Lev

Dans ce morceau de code, vous effectuez deux actions sur RDD

 transformedMessages.foreachRDD(record -> {
        //System.out.println("Aman" +record.count());
        StructType schema = 
        DataTypes.createStructType(fields);

        Dataset ds = ss.createDataFrame(records, schema);
        ds.createOrReplaceTempView("trades");

        System.out.println(ds.count());
        ds.show();

    });

Deux consommateurs de Consumer Group ont essayé de lire la partition de sujet Kafka, mais Kafka permet à un seul consommateur d'un groupe de consommateurs de lire la partition de sujet Kafka. La solution à ce problème est la suivante: mettre en cache le RDD

 transformedMessages.foreachRDD(record -> {
        //System.out.println("Aman" +record.count());
        StructType schema = 
        DataTypes.createStructType(fields);

        Dataset ds = ss.createDataFrame(records, schema);
        ds.cache()

        System.out.println(ds.count());
        ds.show();

    });
0
Naga

Il s'agit d'un problème similaire de Java.util.ConcurrentModificationException: KafkaConsumer n'est pas sécurisé pour les accès multithreads , plusieurs threads s'exécutant avec le même consommateur et Kafka ne prend pas en charge le multithreading. Assurez-vous également que vous n'utilisez pas spark.speculation = true car cela entraînera l'erreur mentionnée ci-dessus.

0
Alfonso Elizalde

Comme décrit dans ce rapport de bogue: https://issues.Apache.org/jira/browse/SPARK-19185 , il s’agit d’un problème connu de Spark/Kafka. 

Dans mon cas, je vais éviter d'utiliser window et d'utiliser le partitionnement en combinaison avec batchInterval et blockInterval, comme décrit ici: https://spark.Apache.org/docs/latest/streaming-programming-guide.html# niveau de parallélisme dans la réception de données

0
Sergey