15、Spark Streaming源码解读之No Receivers彻底思考
在前几期文章里讲了带Receiver的Spark Streaming 应用的相关源码解读,但是现在开发Spark Streaming的应用越来越多的采用No Receivers(Direct Approach)的方式,No Receiver的方式的优势:
1. 更强的控制自由度
2. 语义一致性
object DirectKafkaWordCount {def main(args: Array[String]) {if (args.length < 2) {System.err.println(s"""|Usage: DirectKafkaWordCount <brokers> <topics>| <brokers> is a list of one or more Kafka brokers| <topics> is a list of one or more kafka topics to consume from|""".stripMargin)System.exit(1)}StreamingExamples.setStreamingLogLevels()val Array(brokers, topics) = args// Create context with 2 second batch intervalval sparkConf = new SparkConf().setAppName("DirectKafkaWordCount")val ssc = new StreamingContext(sparkConf, Seconds(2))// Create direct kafka stream with brokers and topicsval topicsSet = topics.split(",").toSetval kafkaParams = Map[String, String]("metadata.broker.list" -> brokers)val messages = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topicsSet)// Get the lines, split them into words, count the words and printval lines = messages.map(_._2)val words = lines.flatMap(_.split(" "))val wordCounts = words.map(x => (x, 1L)).reduceByKey(_ + _)wordCounts.print()// Start the computationssc.start()ssc.awaitTermination()}}
/*** A batch-oriented interface for consuming from Kafka.* Starting and ending offsets are specified in advance,* so that you can control exactly-once semantics.* @param kafkaParams Kafka <a href="http://kafka.apache.org/documentation.html#configuration">* configuration parameters</a>. Requires "metadata.broker.list" or "bootstrap.servers" to be set* with Kafka broker(s) specified in host1:port1,host2:port2 form.* @param offsetRanges offset ranges that define the Kafka data belonging to this RDD* @param messageHandler function for translating each message into the desired type*/private[kafka]class KafkaRDD[K: ClassTag,V: ClassTag,U <: Decoder[_]: ClassTag,T <: Decoder[_]: ClassTag,R: ClassTag] private[spark] (sc: SparkContext,kafkaParams: Map[String, String],val offsetRanges: Array[OffsetRange], //该RDD的数据偏移量leaders: Map[TopicAndPartition, (String, Int)],messageHandler: MessageAndMetadata[K, V] => R) extends RDD[R](sc, Nil) with Logging with HasOffsetRanges
trait HasOffsetRanges {def offsetRanges: Array[OffsetRange]}
inal class OffsetRange private(val topic: String,val partition: Int,val fromOffset: Long,val untilOffset: Long) extends Serializable
override def getPartitions: Array[Partition] = {offsetRanges.zipWithIndex.map { case (o, i) =>val (host, port) = leaders(TopicAndPartition(o.topic, o.partition))new KafkaRDDPartition(i, o.topic, o.partition, o.fromOffset, o.untilOffset, host, port)}.toArray}
private[kafka]class KafkaRDDPartition(val index: Int,val topic: String,val partition: Int,val fromOffset: Long,val untilOffset: Long,val host: String,val port: Int) extends Partition {/** Number of messages this partition refers to */def count(): Long = untilOffset - fromOffset}
KafkaRDDPartition清晰的描述了数据的具体位置,每个KafkaRDDPartition分区的数据交给KafkaRDD的compute方法计算:
override def compute(thePart: Partition, context: TaskContext): Iterator[R] = {val part = thePart.asInstanceOf[KafkaRDDPartition]assert(part.fromOffset <= part.untilOffset, errBeginAfterEnd(part))if (part.fromOffset == part.untilOffset) {log.info(s"Beginning offset ${part.fromOffset} is the same as ending offset " +s"skipping ${part.topic} ${part.partition}")Iterator.empty} else {new KafkaRDDIterator(part, context)}}
private class KafkaRDDIterator(part: KafkaRDDPartition,context: TaskContext) extends NextIterator[R] {context.addTaskCompletionListener{ context => closeIfNeeded() }log.info(s"Computing topic ${part.topic}, partition ${part.partition} " +s"offsets ${part.fromOffset} -> ${part.untilOffset}")val kc = new KafkaCluster(kafkaParams)val keyDecoder = classTag[U].runtimeClass.getConstructor(classOf[VerifiableProperties]).newInstance(kc.config.props).asInstanceOf[Decoder[K]]val valueDecoder = classTag[T].runtimeClass.getConstructor(classOf[VerifiableProperties]).newInstance(kc.config.props).asInstanceOf[Decoder[V]]val consumer = connectLeadervar requestOffset = part.fromOffsetvar iter: Iterator[MessageAndOffset] = null- //..................
- }
val messages = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topicsSet)
def createDirectStream[K: ClassTag,V: ClassTag,KD <: Decoder[K]: ClassTag,VD <: Decoder[V]: ClassTag] (ssc: StreamingContext,kafkaParams: Map[String, String],topics: Set[String]): InputDStream[(K, V)] = {val messageHandler = (mmd: MessageAndMetadata[K, V]) => (mmd.key, mmd.message)- //创建KakfaCluster对象
val kc = new KafkaCluster(kafkaParams)- //更具kc的信息获取数据偏移量
val fromOffsets = getFromOffsets(kc, kafkaParams, topics)new DirectKafkaInputDStream[K, V, KD, VD, (K, V)](ssc, kafkaParams, fromOffsets, messageHandler)}
override def compute(validTime: Time): Option[KafkaRDD[K, V, U, T, R]] = {//计算最近的数据终止偏移量val untilOffsets = clamp(latestLeaderOffsets(maxRetries))- //利用数据的偏移量创建KafkaRDD
val rdd = KafkaRDD[K, V, U, T, R](context.sparkContext, kafkaParams, currentOffsets, untilOffsets, messageHandler)// Report the record number and metadata of this batch interval to InputInfoTracker.val offsetRanges = currentOffsets.map { case (tp, fo) =>val uo = untilOffsets(tp)OffsetRange(tp.topic, tp.partition, fo, uo.offset)}val description = offsetRanges.filter { offsetRange =>// Don't display empty ranges.offsetRange.fromOffset != offsetRange.untilOffset}.map { offsetRange =>s"topic: ${offsetRange.topic}\tpartition: ${offsetRange.partition}\t" +s"offsets: ${offsetRange.fromOffset} to ${offsetRange.untilOffset}"}.mkString("\n")// Copy offsetRanges to immutable.List to prevent from being modified by the userval metadata = Map("offsets" -> offsetRanges.toList,StreamInputInfo.METADATA_KEY_DESCRIPTION -> description)val inputInfo = StreamInputInfo(id, rdd.count, metadata)ssc.scheduler.inputInfoTracker.reportInfo(validTime, inputInfo)currentOffsets = untilOffsets.map(kv => kv._1 -> kv._2.offset)Some(rdd)}
15、Spark Streaming源码解读之No Receivers彻底思考的更多相关文章
- Spark Streaming源码解读之No Receivers彻底思考
本期内容 : Direct Acess Kafka Spark Streaming接收数据现在支持的两种方式: 01. Receiver的方式来接收数据,及输入数据的控制 02. No Receive ...
- Spark Streaming源码解读之JobScheduler内幕实现和深度思考
本期内容 : JobScheduler内幕实现 JobScheduler深度思考 JobScheduler 是整个Spark Streaming调度的核心,需要设置多线程,一条用于接收数据不断的循环, ...
- Spark Streaming源码解读之流数据不断接收和全生命周期彻底研究和思考
本节的主要内容: 一.数据接受架构和设计模式 二.接受数据的源码解读 Spark Streaming不断持续的接收数据,具有Receiver的Spark 应用程序的考虑. Receiver和Drive ...
- Spark Streaming源码解读之流数据不断接收全生命周期彻底研究和思考
本期内容 : 数据接收架构设计模式 数据接收源码彻底研究 一.Spark Streaming数据接收设计模式 Spark Streaming接收数据也相似MVC架构: 1. Mode相当于Rece ...
- Spark Streaming源码解读之Receiver生成全生命周期彻底研究和思考
本期内容 : Receiver启动的方式设想 Receiver启动源码彻底分析 多个输入源输入启动,Receiver启动失败,只要我们的集群存在就希望Receiver启动成功,运行过程中基于每个Tea ...
- Spark Streaming源码解读之生成全生命周期彻底研究与思考
本期内容 : DStream与RDD关系彻底研究 Streaming中RDD的生成彻底研究 问题的提出 : 1. RDD是怎么生成的,依靠什么生成 2.执行时是否与Spark Core上的RDD执行有 ...
- Spark Streaming源码解读之Job动态生成和深度思考
本期内容 : Spark Streaming Job生成深度思考 Spark Streaming Job生成源码解析 Spark Core中的Job就是一个运行的作业,就是具体做的某一件事,这里的JO ...
- 16.Spark Streaming源码解读之数据清理机制解析
原创文章,转载请注明:转载自 听风居士博客(http://www.cnblogs.com/zhouyf/) 本期内容: 一.Spark Streaming 数据清理总览 二.Spark Streami ...
- 11.Spark Streaming源码解读之Driver中的ReceiverTracker架构设计以及具体实现彻底研究
上篇文章详细解析了Receiver不断接收数据的过程,在Receiver接收数据的过程中会将数据的元信息发送给ReceiverTracker: 本文将详细解析ReceiverTracker的的架构 ...
随机推荐
- STM32自动生成精美图案
http://note.youdao.com/noteshare?id=65f237225624d22fe18f4aaaeec8db07
- window.location.hash在firefox下中文自动转码为UTF-8问题
1.window.location.hash window.location.hash这个属性主要是读取和写入网页位置的,我们经常会用来控制网页单页面跳转或者是控制网页位置.然而这个属性在firefo ...
- Java设计模式の模版方法模式
概述 模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的 ...
- Spring Boot 使用IntelliJ IDEA创建一个web开发实例(一)
.新建项目File-->New-->Project-->Spring Initializr 点击Finish,一个Spring Boot web应用就创建好了.
- Linux rpm yum 等安装软件
任何程序都是先写代码,拿到源码去编译得到一个目标程序. 1 编译的过程复杂有需要准备编译的环境,和硬件有关,32位64位,内核的不同等等所以需要编译多次 Java特殊但是他需要安装jvm, ...
- Distributed Systems 分布式系统
先来扯淡,几天是14年12月31日了,茫茫然,2014就剩最后一天了.这两天国大都放假,我给自己安排了四篇博客欠账,这就是其中的第一篇,简单介绍一些分布式系统的一些概念和设计思想吧.后面三篇分别是Ne ...
- Linux Shell 程序调试
Linux Shell 程序调试 Shell程序的调试是通过运行程序时加入相关调试选项或在脚本程序中加入相关语句,让shell程序在执行过程中显示出一些可供参考的“调试信息”.当然,用户也可以在she ...
- mysql 导出数据导致锁表
故事原由:今天同事小星做系统优化时问我一个sql问题,为什么查询慢,我看了一眼,发现用到了表A中的datetime类型列进行时间比较,建议他给datetime类型列加上索引. 可这是生产库,表A里面有 ...
- Web 开发者易犯的5大严重错误
无论你是编程高手,还是技术爱好者,在进行Web开发过程中,总避免不了犯各种各样的错误. 犯了错误,可以改正.但如果犯了某些错误,则会带来重大损失.遗憾.令人惊讶的是,这些错误往往是最普通,最容易避免. ...
- Lua的工具资源3
[LuaSrcDiet] (5.0.2) - 通过删除不必要的空白和注释缩减Lua文件的大小. [LuaProfiler] (5.0) - 一个用来查找Lua应用瓶颈的工具time profiler ...