最近在測試sparkstreaming的時候發現了一個問題,記錄一下
環境 spark 2.x, kafka_0.10.x
示例代碼:
val ssc: StreamingContext = new StreamingContext(sparkSession.sparkContext,Seconds(5))
val kafkaBrokers:String = "hadoop01:9092,hadoop02:9092,hadoop03:9092"
val kafkaTopics: String = "test"
val kafkaParam = Map(
"bootstrap.servers" -> kafkaBrokers,//用於初始化鏈接到集羣的地址
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "group1",
"auto.offset.reset" -> "earliest",
"enable.auto.commit" -> (false: java.lang.Boolean)
)
val inputDStream=KafkaUtils.createDirectStream[String,String](ssc, LocationStrategies.PreferConsistent,
ConsumerStrategies.Subscribe[String,String](Array(kafkaTopics),kafkaParam))
val valueDStream: DStream[String] = inputDStream.map(_.value())
valueDStream.foreachRDD(rdd =>{
val tRDD: RDD[String] = rdd.filter(_.contains("t"))
val hRDD: RDD[String] = rdd.filter(_.contains("h"))
tRDD.union(hRDD).foreach(println)
})
報錯信息.java.util.ConcurrentModificationException: KafkaConsumer is not safe for multi-threaded access
原因分析:
這裏的兩個rdd讀取的是同一份數據,當執行action時,都會觸發兩次數據的讀操作,(rdd中的一個分區對應着topic中的一個分區,也就是說kafka中的一個分區的數據這裏被讀取了2次) 但是,同一個分區的數據只能被一個consumer消費,所以這裏報錯。
解決方法:一個可行的解決方案是對rdd進行緩存或者checkpoint,然後要能保證,原始的kafka中的數據,只會被消費一次,然後剩下的數據消費都從緩存中獲取數據。
示例代碼:
val valueDStream: DStream[String] = inputDStream.map(_.value()).persist(StorageLevel.DISK_ONLY)
valueDStream.foreachRDD(rdd =>{
val tRDD: RDD[String] = rdd.filter(_.contains("t"))
val hRDD: RDD[String] = rdd.filter(_.contains("h"))
tRDD.union(hRDD).foreach(println)
})
參考:java.util.ConcurrentModificationException: KafkaConsumer is not safe for multi-threaded access
解決KafkaConsumer多線程接入不安全問題(spark streaming 消費kafka)