【问题标题】:spark streaming dataframes and accumulators on java在 java 上触发流数据帧和累加器
【发布时间】:2020-10-07 00:37:36
【问题描述】:

我正在 Spark Structured Streaming 中处理一个 kafka JSON 流。作为微批次处理,我可以将累加器与流数据帧一起使用吗?

LongAccumulator longAccum = new LongAccumulator("my accum");

Dataset<Row> df2 = df.filter(output.col("Called number").equalTo("0860"))
            .groupBy("Calling number").count();
// put row counter to accumulator for example
df2.javaRDD().foreach(row -> {longAccumulator.add(1);})

抛出

Exception in thread "main" org.apache.spark.sql.AnalysisException: Queries with streaming sources must be executed with writeStream.start();;

。我也很困惑以这种方式使用累加器。将数据帧转换为 RDD 看起来很奇怪且不必要。我可以在没有 RDD 和 foreach() 的情况下完成吗?

根据例外,我从源数据帧中删除了 foreach 并在 writeStream().foreachBatch() 中完成

        StreamingQuery ds = df2
            .writeStream().foreachBatch( (rowDataset, aLong) -> {
                longAccum.add(1);
                log.info("accum : " + longAccum.value());
            })
            .outputMode("complete")
            .format("console").start();

它正在工作,但我在日志中没有值,并且在 GUI 中看不到累加器。

【问题讨论】:

    标签: java apache-spark streaming accumulator


    【解决方案1】:

    不,您可以使用以下数据集直接访问-

     LongAccumulator longAccum = spark.sparkContext().longAccumulator("my accum");
    
    
            Dataset<Row> df = spark.range(100).withColumn("x", lit("x"));
    
            //access in map
            df.map((MapFunction<Row, Row>) row -> {
                longAccum.add(1);
                return  row;
            }, RowEncoder.apply(df.schema()))
                    .count();
    
            // accumulator value
            System.out.println(longAccum.value()); // 100
    
            longAccum.reset();
            // access in for each
            df.foreach((ForeachFunction<Row>) row -> longAccum.add(1));
    
            // accumulator value
            System.out.println(longAccum.value()); // 100
    

    请注意,累加器值仅在执行action 时更新。

    使用流式数据帧

     longAccum.reset();
            /**
             * streaming dataframe from csv dir
             * test.csv
             * --------
             * csv
             * id,name
             * 1,bob
             * 2,smith
             * 3,jam
             * 4,dwayne
             * 5,mike
             */
            String fileDir = getClass().getResource("/" + "csv").getPath();
            StructType schema = new StructType()
                    .add(new StructField("id", DataTypes.LongType, true, Metadata.empty()))
                    .add(new StructField("name", DataTypes.StringType, true, Metadata.empty()));
            Dataset<Row> json = spark.readStream().schema(schema).option("header", true).csv(fileDir);
    
            StreamingQuery streamingQuery = json
                    .map((MapFunction<Row, Row>) row -> {
                        longAccum.add(1);
                        return row;
                    }, RowEncoder.apply(df.schema()))
                    .writeStream()
                    .format("console").start();
            streamingQuery.processAllAvailable();
    
            // accumulator value
            System.out.println(longAccum.value()); // 5
    

    【讨论】:

    • map() 和 foreach() 方式抛出相同的异常,因为这是流数据帧
    • 谢谢,但为什么 foreachBatch() 不起作用?它只是 Dataframe 的解决方案吗?
    • 添加 streamingQuery.processAllAvailable(); 发布您的 writestream 声明。检查我的答案以供参考。如果有帮助请点赞+采纳
    猜你喜欢
    • 2015-12-09
    • 1970-01-01
    • 1970-01-01
    • 2014-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多