spark
spark基础知识
spark的任务提交流程
shuffle过程分析
rdd的特点与五大属性
spark整合kafka
1、SparkStreaming + Kafka ----Receiver
用的是Kafka高层次的消费者api,不能自己维护offset
object Sparkkafka08ReceiverDStream {
def main(args: Array[String]): Unit = {
val sparkContext = new SparkContext(new SparkConf()
.setMaster("local[6]")
.setAppName("08ReceiverDstream")
.set("spark.streaming.receiver.writeAheadLog.enable","true")) //开启WAL预写日志,保存数据源端安全性
sparkContext.setLogLevel("WARN")
val streamingContext = new StreamingContext(sparkContext,Seconds(5))
//zk地址
val zkQuorum = "node-1:2181,node-2:2181,node-3:2181"
//消费组
val groupId = "sparkkafka_group"
//topic相关信息Map[String,Int],topic名称->分区数
val topics = Map("kafkatopic"->3)
//开启3个Receiver接收数据,每一个receiver都是一个独立的线程,去接收一个分区里面的数据
val eachPatitionStream: immutable.IndexedSeq[ReceiverInputDStream[(String, String)]] = (1 to 3).map(x => {
//使用receiverDstream的方式进行消费,消费模式使用的是kafka的HighLevelAPI,offset都保存在zk中
val stream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(streamingContext, zkQuorum, groupId, topics) //只是相当于开启了一个receiver
stream
})
//合并三个分区里面的数据
//DStream里面保存的是一个元组,第一个数据是卡夫卡当中每条数据key,第二个数据是kafka当中每条数据value
val result: DStream[(String, String)] = streamingContext.union(eachPatitionStream)
result.print()
streamingContext.start()
streamingContext.awaitTermination()
}
}
- 流程分析
当SparkStreaming application运行起来的时候,首先executor中会有一个receiver task接收kafka中推送过来的数据,将数据接收过来进行持久化,默认的持久化级别是:Memory_AND_DISK_SER_2,receiver task将数据进行储存和备份,这个过程中,会产生节点之间的数据传输,备份完成后去zookeeper中进行更新偏移量。然后向Driver节点上的receiver tracker汇报数据位置,最后Driver根据数据本地化将task发送到不同的executor上去执行。 - 在执行的过程中,receiver task将数据的偏移量更新到zookeeper中,这时Driver节点宕机了,那么Driver下的Executor进程会被kill 掉,Executor可能有一部分数据未处理,就会存在找不到数据的问题,相当于数据丢失。
问题解决:
开启WAL(write ahead log)预写日志机制,当receiver task接收过来的数据接收到时,会首先备份到HDFS上一份(需要将接收过来的数据的持久化级别降低到MEMORY_ADN_DISK),并且向zookeeper更新信息,以及汇报位置等。这样解决了找不到的问题,但是由于向HDFS中写数据会影响job执行的效率。 - 创建一个receiver接收器来对kafka进行定时拉取数据,这里产生的dstream中rdd分区和kafka的topic分区不是一个概念,故如果增加特定topic的分区数仅仅是增加一个receiver中消费topic的线程数,并没有增加spark的并行处理的数据量。
- Receiver接收数据时,每隔spark.streaming.blockInterval=200ms,将接受类的数据封装到一个block,batchInterval内生成的这些block组成一个batch.
假设batchInterval=5,默认一个batch25个block,这里的每一个block对应着RDD中的一个partition,并行度默认就是25个。 - 如何提高并行度?
在batchInterval一定的情况下,减少spark.streaming.blockInterval,可以增多对应RDD的分区数,并行度可以增加,一般blockInterval值不低于20ms. - receiver弊端
1).采用了receiver接收器模式,将数据接收到executor中再进行处理,尤其是在任务有堆积情况下。内存占用很大。
2).底层采用了高阶的API实现消费kafka,这种模式不关心offset,只要数据,针对需要维护offset的场景不适用。
3).存在丢失数据的问题,开启WAL可以避免,但是增加了数据处理的延迟。
2、SparkStreaming + Kafka ----Direct
Direct模式使用kafka的低阶API进行消费,这种API可以对offset进行操作,Receiver采用zookeeper管理offset,而Direct模式采用Spark自己管理,将offset存贮在topic中。
Direct模式相对于Receiver模式简化了并行度,Direct模式读取topic数据对应的并行度与topic的partition个数相同。也即是rdd的分区数?
kafka0.11之后彻底抛弃了reciver模式,可以手动提交offset,进行offset的管理维护,保证数据不会丢失,推荐使用。
/**
* 使用lowLevelAPI进行消费,数据的offset都保存到topic里面去了
* 这种整合方式不需要手动的开启多个receiver线程去消费数据
*/
object sparkkafka08DirectDStream {
def main(args: Array[String]): Unit = {
val sparkContext = new SparkContext(new SparkConf().setAppName("08DirectDStream").setMaster("local[6]"))
sparkContext.setLogLevel("WARN")
val streamingContext = new StreamingContext(sparkContext,Seconds(5))
//配置kafka相关参数
val kafkaParams = Map("metadata.broker.list"->"node-1:9092,node-2:9092,node-3:9092","group.id"->"Kafka_Direct")
//要消费的topic名字
val topics = Set("kafkatopic")
val result: InputDStream[(String, String)] = KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder](streamingContext,kafkaParams,topics)
val topicDatas: DStream[String] = result.map(x=>x._2)
//topicDatas.print()
val wordNum: DStream[(String, Int)] = topicDatas.flatMap(x=>x.split(" ")).map((_,1)).reduceByKey(_+_)
wordNum.print()
streamingContext.start()
streamingContext.awaitTermination()
}
}
/**
* 通过0.10版本用DirectDStream的方式实现数据的消费
*/
object kafka010DirectDStream {
def main(args: Array[String]): Unit = {
val sparkContext = new SparkContext(new SparkConf().setMaster("local[6]").setAppName("010DirectDStream"))
sparkContext.setLogLevel("WARN")
val streamingContext = new StreamingContext(sparkContext,Seconds(5))
val locationStrategy: LocationStrategy = LocationStrategies.PreferConsistent
val topics = Array("kafkatopic")
val brokers= "node-1:9092,node-2:9092,node-3:9092"
val sourcetopic="kafkatopic"
//创建消费者组
var group="sparkafkaGroup"
//消费者配置
val kafkaParam = Map(
"bootstrap.servers" -> brokers,//用于初始化链接到集群的地址
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
//用于标识这个消费者属于哪个消费团体
"group.id" -> group,
//如果没有初始化偏移量或者当前的偏移量不存在任何服务器上,可以使用这个配置属性
//可以使用这个配置,latest自动重置偏移量为最新的偏移量
"auto.offset.reset" -> "latest",
//如果是true,则这个消费者的偏移量会在后台自动提交
"enable.auto.commit" -> (false: java.lang.Boolean)
);
val consumerStrategy: ConsumerStrategy[String, String] = ConsumerStrategies.Subscribe(topics,kafkaParam)
val result: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String,String](streamingContext,locationStrategy,consumerStrategy)
//循环遍历每一个RDD当中的数据
result.foreachRDD(iter => {
if(iter.count() > 0){