1.foreach***几个算子的比较
foreach:作用于DStream中每一个时间间隔的RDD中的每一个元素。
foreachPartition:作用于每一个时间间隔的RDD。
foreachRDD:作用于DStream中每一个时间间隔RDD。
val orderInfoWithProvinceDstream: DStream[OrderInfo] = orderInfoWithFirstRealFlagDstream.transform { rdd =>
//driver 按批次周期性执行
val sql = "select id,name,area,ise_code,iso_3166_2 from gmall110105_province_info"
val provinceInfoList: List[JSONObject] = PhoenixUtil.queryList(sql)
// 封装广播变量
val provinceMap: Map[String, ProvinceInfo] = provinceInfoList.map { jsonObj =>
val provinceInfo = ProvinceInfo(
jsonObj.getString("ID"),
jsonObj.getString("NAME"),
jsonObj.getString("AREA_CODE"),
jsonObj.getString("ISO_CODE"),
jsonObj.getString("ISO_3166_2"))
(provinceInfo.id, provinceInfo)
}.toMap
val provinceBC: Broadcast[Map[String, ProvinceInfo]] = ssc.sparkContext.broadcast(provinceMap)
// 在executor中执行
val orderInfoWithProvinceRDD: RDD[OrderInfo] = rdd.map { orderInfo => // executor中执行
val provinceMap: Map[String, ProvinceInfo] = provinceBC.value
val provinceInfo: ProvinceInfo = provinceMap.getOrElse(orderInfo.province_id.toString, null)
if (provinceInfo != null) {
orderInfo.province_name = provinceInfo.name
orderInfo.province_area_code = provinceInfo.area_code
orderInfo.province_iso_code = provinceInfo.iso_code
orderInfo.province_iso_3166_2 = provinceInfo.iso_3166_2
}
orderInfo
}
/* rdd.mapPartitions{ xxx => //executor中执行
xxx
}*/
orderInfoWithProvinceRDD
}
2.transfrom与map
transform是流中特有的算子,将DStream[Entity]转换成RDD[Entity]来使用,在driver中执行。
map是将DStream[Entity]转换成Entity。
最后返回的都是DStream[Entity]
例如获取偏移量
val inputGetOffsetDstream = recordInputStream.transform(rdd => {
// 所有分区的结束位置,在driver中执行。周期性执行
offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
rdd
})
3.auto.offset.reset设置
val kafkaParm = collection.mutable.Map(
"bootstarp.servers" -> broker_list,
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "gmall_consumer_group",
// 如果没有初始化偏移量或者当前的偏移量不存在任何服务器上,可以使用这个配置属性
// latest自动重置偏移量为最新的偏移量
"auto.offset.reset" -> "latest",
// 如果是true,则这个消费者的偏移量会在后台自动提交,但是kafka宕机容易丢失数据 true是kafka定时提交偏移量 大概5-10秒
// 因为Zookeeper并不适合大批量的频繁写入操作,新版的kafka已推荐将consumer的位移信息保存在Kafka你内部的topic中。
// 即__consumer_offsets topic,并且默认提供了kafka_consumer_groups.sh脚本供用户查看consumer信息。 偏移量保存在kafka
// 如果是false,会需要手动维护kafka偏移量
"enable.auto.commit" -> (false: java.lang.Boolean)
)
4.KafkaUtils获取Kafka数据的2种API
1、KafkaUtils.createDstream
构造函数为KafkaUtils.createDstream(ssc, [zk], [consumer group id], [per-topic,partitions] ) 使用了receivers来接收数据,利用的是Kafka高层次的消费者api,对于所有的receivers接收到的数据将会保存在spark executors中,然后通过Spark Streaming启动job来处理这些数据,默认会丢失,可启用WAL日志,该日志存储在HDFS上
A、创建一个receiver来对kafka进行定时拉取数据,ssc的rdd分区和kafka的topic分区不是一个概念,故如果增加特定主体分区数仅仅是增加一个receiver中消费topic的线程数,并不增加spark的并行处理数据数量
B、对于不同的group和topic可以使用多个receivers创建不同的DStream
C、如果启用了WAL,需要设置存储级别,即KafkaUtils.createStream(….,StorageLevel.MEMORY_AND_DISK_SER)
2.KafkaUtils.createDirectStream
区别Receiver接收数据,这种方式定期地从kafka的topic+partition中查询最新的偏移量,再根据偏移量范围在每个batch里面处理数据,使用的是kafka的简单消费者api
优点:
A、 简化并行,不需要多个kafka输入流,该方法将会创建和kafka分区一样的rdd个数,而且会从kafka并行读取。
B、高效,这种方式并不需要WAL,WAL模式需要对数据复制两次,
第一次是被kafka复制,另一次是写到wal中
C、恰好一次语义(Exactly-once-semantics),传统的读取kafka数据是通过kafka高层次api把偏移量写入zookeeper中,存在数据丢失的可能性是zookeeper中和ssc的偏移量不一致。EOS通过实现kafka低层次api,偏移量仅仅被ssc保存在checkpoint中,消除了zk和ssc偏移量不一致的问题。缺点是无法使用基于zookeeper的kafka监控工具
5.KafkaUtils.createDirectStream参数详解
def getKafkaStream(topic: String, ssc: StreamingContext, groupId: String): InputDStream[ConsumerRecord[String, String]] = {
//LocationStrategies.PreferConsistent任务尽量均匀分布在各个executor节点
// 创建DStream,返回接收到的输入数据
// LocationStrategies:根据给定的主题和集群地址创建consumer
// LocationStrategies.PreferConsistent:持续的在所有Executor之间分配分区
// ConsumerStrategies:选择如何在Driver和Executor上创建和配置Kafka Consumer
// ConsumerStrategies.Subscribe:订阅一系列主题
//createDirectStream[String,String] 指定消费kafka的message的key/value的类型
//ConsumerStrategies.Subscribe[String,String]指定key/value的类型
val dStream = KafkaUtils.createDirectStream[String, String](ssc, LocationStrategies.PreferConsistent, ConsumerStrategies.Subscribe[String, String](Array(topic), kafkaParm))
dStream
}