org.apache.spark.rdd.MapPartitionsRDD cannot be cast to org.apache.spark.streaming.kafka010.HasOffse

版权声明:原创文章,转载请注明出处 https://blog.csdn.net/xianpanjia4616/article/details/85871063

最近有很多同学来问我这个问题,说我的代码啥也没改呀,昨天晚上还运行的好好的,第二天早上再运行就报错了,org.apache.spark.rdd.MapPartitionsRDD cannot be cast to org.apache.spark.streaming.kafka010.HasOffsetRanges,怎么都运行不了,这个错相信大家都非常的熟悉,就是一个类型转换异常,从报错上看呢,说是MapPartitionsRD不能转换成HasOffsetRanges,这个错在什么情况下才会出现呢?,先看下面的代码(只粘贴了一部分)

 val word = kafkaStreams.map(_.value()).flatMap(_.split(" ")).map((_,1))
    word.foreachRDD(rdd => {
      if(!rdd.isEmpty()) {
        val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges

 其实这个错发生在获取偏移量的时候,在spark中获取偏移量只用rdd.asInstanceOf[HasOffsetRanges].offsetRanges这一句话就可以了,简单说一下就是先把rdd强制类型转换成HasOffsetRanges,然后调用HasOffsetRanges里面的offsetRanges这个方法返回一个Array[OffsetRange]数组.下面带大家分析一下源码,就理解了为什么这个地方会报错.

首先这个地方的rdd是一个KafkaRDD,然后我们来看一下KafkaRDD的源码如下:

private[spark] class KafkaRDD[K, V](
    sc: SparkContext,
    val kafkaParams: ju.Map[String, Object],
    val offsetRanges: Array[OffsetRange],
    val preferredHosts: ju.Map[TopicPartition, String],
    useConsumerCache: Boolean
) extends RDD[ConsumerRecord[K, V]](sc, Nil) with Logging with HasOffsetRanges {

 可以看到呢KafkaRDD继承了(RDD[ConsumerRecord[K, V]](sc, Nil) with Logging with HasOffsetRanges),拥有了他们两个的特性,这里的RDD里面是ConsumerRecord[K,V]类型,然后我们在看一下ConsumerRecord的源码如下:

 private final String topic;
    private final int partition;
    private final long offset;
    private final long timestamp;
    private final TimestampType timestampType;
    private final long checksum;
    private final int serializedKeySize;
    private final int serializedValueSize;
    private final K key;
    private final V value;

 可以看到这里面有topic,partition,offest,timestamp,key,value,等属性,这个value就是我们写入kafka的数据.然后我们看一下HasOffsetRanges这个类,进去会发现这是一个接口,它带有返回OffsetRange数组的单个方法,他的实现类就是KafkaRDD,允许你在每个分区的基础上获取主题和偏移量信息,如下所示:

trait HasOffsetRanges {
  def offsetRanges: Array[OffsetRange]
}

KafkaRDD实现了RDD里面的一个方法叫getPartitions,如下所示:

override def getPartitions: Array[Partition] = {
    offsetRanges.zipWithIndex.map { case (o, i) =>
        new KafkaRDDPartition(i, o.topic, o.partition, o.fromOffset, o.untilOffset)
    }.toArray
  }

getPartitions方法使用数组中的每个OffsetRange,这里也意味着kafka的分区和RDD的分区是一对一的关系.

我们在回头看下上面的代码,之所以报错是因为这里需要的是RDD[ConsumerRecord[K,V]]类型,因为offest的信息都在这里面,但是上面的代码已经转换成RDD[(String,Int)]类型,这个里面没有offest的信息,所以也转换不成HasOffsetRanges这个类型,就报错了.看到这里大家应该明白了吧.

如果有写的不对的地方,欢迎大家指正,如果有什么疑问,可以加QQ群:340297350,谢谢

 

猜你喜欢

转载自blog.csdn.net/xianpanjia4616/article/details/85871063