Spark Streaming从Kafka中接收数据的两种方式

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_41907511/article/details/84869597

spark streaming流式处理kafka中的数据,首先是把数据接收过来,然后转换为spark streaming中的数据结构Dstream。接收数据的方式有两种:1.利用Receiver接收数据;2.直接从kafka读取数据。

基于Receiver的方式(旧方法)

流程:
此方法使用Receiver接收数据。Receiver是使用Kafka高阶API接口实现的。与所有接收器一样,从Kafka通过Receiver接收的数据存储在Spark执行器中,然后由Spark Streaming启动的作业处理数据。
在这里插入图片描述
问题:
在默认配置下,此方法可能会在失败时丢失数据。为确保零数据丢失,必须在Spark Streaming中另外启用预写日志(Write Ahead Logs)。这将同步保存所有收到的Kafka数据到分布式文件系统(例如HDFS)上,以便在发生故障时可以恢复所有数据。

注意点:
在Receiver的方式中,Kafka中的topic partition与Spark Streaming中生成的RDD partition无关。所以如果我们加大每个topic的partition数量,仅仅是增加线程来处理由单一Receiver消费的主题。但是这并没有增加Spark在处理数据上的并行度。
对于不同的Group和topic我们可以使用多个Receiver创建不同的Dstream来并行接收数据,之后可以利用union来统一成一个Dstream。
如果我们启用了Write Ahead Logs复制到文件系统如HDFS,那么storage level需要设置成 StorageLevel.MEMORY_AND_DISK_SER,也就是KafkaUtils.createStream(…, StorageLevel.MEMORY_AND_DISK_SER)

直接读取方式( Direct Stream方法)

流程:
这种方法不使用接收器(Receiver)来接收数据,而是定期向Kafka查询每个主题的每个分区中的最新偏移量(offsets),并相应地定义要在每个批次(batch)中处理的偏移量范围。当Spark Streaming启动处理数据的作业时,利用Kafka的低阶API读取Kafka定义的偏移范围的数据。
在这里插入图片描述
优点:
这种方法相较于Receiver方式的优势在于:
简化的并行:在Receiver的方式中我们提到创建多个Receiver之后利用union来合并成一个Dstream的方式提高数据传输并行度。而在Direct方式中,Kafka中的partition与RDD中的partition是一一对应的并行读取Kafka数据,这种映射关系也更利于理解和优化。
高效:在Receiver的方式中,为了达到0数据丢失需要将数据存入Write Ahead Log中,这样在Kafka和日志中就保存了两份数据,浪费!而第二种方式不存在这个问题,只要我们Kafka的数据保留时间足够长,我们都能够从Kafka进行数据恢复。
精确一次:在Receiver的方式中,使用的是Kafka的高阶API接口从Zookeeper中获取offset值(偏移量),这也是传统的从Kafka中读取数据的方式,但由于Spark Streaming消费的数据和Zookeeper中记录的offset不同步,这种方式偶尔会造成数据重复消费。而第二种方式,直接使用了简单的低阶Kafka API,Offsets则利用Spark Streaming的checkpoints进行记录,消除了这种不一致性。

缺点:
Direct需要用户采用checkpoint或者第三方存储来维护offsets,而不像Receiver-based那样,通过ZooKeeper来维护Offsets,此提高了用户的开发成本

/**
  * Kafka 0.10的Spark Streaming集成(spark获取kafka数据的最新方式)
  */
object KafkaDirectStream {

def main(args: Array[String]): Unit = {
//创建SparkConf,如果将任务提交到集群中,那么要去掉.setMaster(“local[2]”)

val conf = new SparkConf().setAppName("DirectStream").setMaster("local[2]")
val sc = new SparkContext(conf)
sc.setLogLevel("WARN")
//创建一个StreamingContext,其里面包含了一个SparkContext
val streamingContext = new StreamingContext(sc, Seconds(5))

//配置kafka的参数
/**
  * Kafka服务监听端口
  * 指定kafka输出key的数据类型及编码格式(默认为字符串类型编码格式为uft-8)
  * 指定kafka输出value的数据类型及编码格式(默认为字符串类型编码格式为uft-8)
  * 消费者ID,随意指定
  * 指定从latest(最新)还是smallest(最早)处开始读取数据
  * 如果true,consumer定期地往zookeeper写入每个分区的offset
  */
val kafkaParams = Map[String, Object](

  "bootstrap.servers" -> "192.168.2.210:9092",    //kafka机器IP:端口
  "key.deserializer" -> classOf[StringDeserializer],
  "value.deserializer" -> classOf[StringDeserializer],
  "group.id" -> "g1",
  "auto.offset.reset" -> "latest",
  "partition.assignment.strategy" -> "org.apache.kafka.clients.consumer.RangeAssignor",
  "enable.auto.commit" -> (false: java.lang.Boolean)

)

//要监听的Topic,可以同时监听多个
val topics = Array("test")

//在Kafka中记录读取偏移量
val stream = KafkaUtils.createDirectStream[String, String](
  streamingContext,
  //位置策略(可用的Executor上均匀分配分区)
  LocationStrategies.PreferConsistent,
  //消费策略(订阅固定的主题集合)
  ConsumerStrategies.Subscribe[String, String](topics, kafkaParams)
)


//迭代DStream中的RDD(KafkaRDD),将每一个时间点对应的RDD取出来
stream.foreachRDD { rdd =>
  //获取该RDD对应的偏移量
  val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
  //取出对应的数据
  rdd.foreach{ line =>
    println(line.key() + " " + line.value())
  }

  //异步更新偏移量到kafka中
  // some time later, after outputs have completed
  stream.asInstanceOf[CanCommitOffsets].commitAsync(offsetRanges)
}
streamingContext.start()
streamingContext.awaitTermination()

}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值