Spark structured streaming+kafka 数据输入与输出不相等

前言

最新在写structured streaming的程序,发现kafka输入和输出的数据不成比例,正常如果你的group里只有一个消费者,那么应该输入和输出是相等的才对。

但是我的生产者和消费者2边的数据不一样,如下图:

在这里插入图片描述

我的2个 topic,test19和test20可以看到2个程序的输出数据基本是输入的10倍,这肯定有问题,因此我要做几个实验,来验证到底是哪里除了问题,争取解决此问题。

实验1:

实现过程:从kafka获取输入后直接输出到另外一个topic里:

实验一代码:
数据生产与消费不写了,
基本流程是:
java生产者->kafka_topic34->spark消费者->kafka_topic35->java消费者

Spark代码:Scala

import org.apache.spark.sql.SparkSession

object KafkaOutputDataSizeTest {
    
    
  def main(args: Array[String]): Unit = {
    
    
    //解决找不到HADOOP环境问题
    System.setProperty("hadoop.home.dir", "C://hadoop-3.1.1")

    //初始化spark
    val spark = SparkSession
      .builder
      .appName("KafkaOutputDataSizeTest")
      .master("local[*]")
      .getOrCreate()

    //这行是必须的,好像用于类型转换
    val df = spark
      .readStream
      .format("kafka")
      .option("kafka.bootstrap.servers", "cdpcluster-1.futuremove.cn:9092,cdpcluster-2.futuremove.cn:9092,cdpcluster-3.futuremove.cn:9092")
      .option("subscribe", "test34")
      .load()
    //    var df2 =df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
    df.writeStream
      .format("kafka")
      .option("checkpointLocation", "test34_point")
      .option("kafka.bootstrap.servers", "cdpcluster-1.futuremove.cn:9092,cdpcluster-2.futuremove.cn:9092,cdpcluster-3.futuremove.cn:9092")
      .option("topic", "test34")
      .start().awaitTermination()
  }
}


实现结果:

输入和输出量级一样
在这里插入图片描述

实验2:

实现过程:做一个简单的filter,将数据1分2

    val df = spark
      .readStream
      .format("kafka")
      .option("kafka.bootstrap.servers", "cdpcluster-1:9092,cdpcluster-2:9092,cdpcluster-3:9092")
      .option("subscribe", "test36")
      .load()
    var df2 = df.selectExpr("CAST(value AS STRING)").as[(String)]
    var df3 = df2.map(x => {
    
    
      //      println(x)
      val obj = JSON.parseObject(x)
      KafkaTestJsonBean(obj.getString("id"), obj.getString("message"))
    })
    //    df2.map(x => {
    
    
    //      val obj = JSON.parseObject(x)
    //
    //    })
    //    df.show()
    df3.writeStream.foreachBatch {
    
     (batchDF: Dataset[KafkaTestJsonBean], batchId: Long) =>
      //如果没有对应报文就不去执行插入数据库操作否则会频繁的建立hbase数据库连接
      val df_1 = batchDF.filter(x => {
    
    
        x.key.toInt % 2 == 0
      })
      val df_2 = batchDF.filter(x => {
    
    
        x.key.toInt % 2 == 1
      })
      df_1.write
        .format("kafka")
        .option("checkpointLocation", "test37_point")
        .option("kafka.bootstrap.servers", "cdpcluster-1:9092,cdpcluster-2:9092,cdpcluster-3:9092")
        .option("topic", "test37").save()
      df_2.write
        .format("kafka")
        .option("checkpointLocation", "test37_point")
        .option("kafka.bootstrap.servers", "cdpcluster-1:9092,cdpcluster-2:9092,cdpcluster-3:9092")
        .option("topic", "test37").save()
//      batchDF.unpersist()
//      batchDF2.unpersist()
    }.start().awaitTermination()

实现结果:

输入不变的情况下,输出是输入的2倍,再加一个filter数据就是3倍。

产生原因:

Spark里action和transform操作,执行action的时候会把这个action的一串transform都计算一遍,也就是一个filter相当于入去了输出的时候相当于读取了一遍kafka,第2个filter结果集保存的时候又读取了一遍kafka.所以相当于读取了2遍kafka.

最终结论:

可以看一下Spark里action和transform操作的说明,官方也给了处理办法。就是缓存,persist()函数.
以下代码来自官网Spark2.4.5,
加入batchDF.persist()这行之后,输入与输出终于一致了。
但是别忘了释放缓存batchDF.unpersist(),我做了个测试,一致不调用unpersist最后我内存溢出了。总之参考官方的写法就可以了。

streamingDF.writeStream.foreachBatch {
    
     (batchDF: DataFrame, batchId: Long) =>
  batchDF.persist()
  batchDF.write.format(...).save(...)  // location 1
  batchDF.write.format(...).save(...)  // location 2
  batchDF.unpersist()
}

官网地址:
http://spark.apache.org/docs/2.4.5/structured-streaming-programming-guide.html

猜你喜欢

转载自blog.csdn.net/lwb314/article/details/115399210