Discretized Stream 也叫 DStream) 是 Spark Streaming 对于持续数据流的一种基本抽象,在内部实现上,DStream 会被表示成一系列连续的 RDD(弹性分布式数据集),每一个 RDD 都代表一定时间间隔内到达的数据。(并不是说DStream的元素是RDD,而是表现和方法都和RDD类似,DStream可以说约等于RDD,因此在操作中,很多人都把DStream当做RDD,甚至喜欢直接命名为RDD。如果打开DStream.scala和RDD.scala,可以发现几乎RDD上的所有operation在DStream中都有相应的定义。) (有些操作也可以把DStream的元素封装成RDD处理,如foreachRDD把批次内的数据当做一个RDD。但是从DStream的类型可以看到第一种情况更普遍,DStream是RDD的模板,如:DStream[(String,Double)],而非DStream[RDD(String,Double)])所以在对 DStream 进行操作时,会被 Spark Stream 引擎转化成对底层 RDD 的操作。对 Dstream 的操作类型有:
关于 DStream Operations 的更多信息,请参考 Spark 官网的 Spark Streaming Programing Guide。
DStream是抽象类,它把连续的数据流拆成很多的小RDD数据块, 这叫做“微批次”, spark的流式处理, 都是“微批次处理”。 DStream内部实现上有批次处理时间间隔,滑动窗口等机制来保证每个微批次的时间间隔里, 数据流以RDD的形式发送给spark做进一步处理。因此, 在一个为批次的处理时间间隔里(BatchDuration), DStream只产生一个RDD。
可以这么理解, DStream.foreachRDD 是一个输出操作符,它操作的不是RDD里的一行数据, 而是操作DStream后面的RDD,在一个时间间隔里, 只返回一个RDD的“微批次”, 为了访问这个“微批次”RDD里的数据, 我们还需要在RDD数据对象上做进一步操作.
重点:Spark Streaming的foreachRDD运行在Driver端,而rdd的foreach和foreachPartion运行在Worker节点。
备注:对数据的向外输出,还是用foreach**算子好,不要用Map**算子,因为Map还要返回一个RDD。
RecivieerInputDStream继承自InputDStream继承自DStream,前两个没有定义几个自己的方法, RecivieerInputDStream.map返回的是DStream类型。
同PairRDDFunctions一样PairDStreamFunctions也提供了很多处理pair类型DSTream的方法,如常用的updateStateByKey,reduceByKeyAndWindow,join等,详见:
https://github.com/apache/spark/blob/master/streaming/src/main/scala/org/apache/spark/streaming/dstream/PairDStreamFunctions.scala
目前项目中的spark版本是1.5的,据说1.6版本中的mapWithState 性能较updateStateByKey提升10倍。有机会了解了解
http://technicaltidbit.blogspot.com/2015/11/spark-streaming-16-stop-using.html
网上很多用updateStateByKey的例程用:
这里实际返回的是Iterator[Some((K,V))],不知他们是否会跑出正确的结果,因为这个函数源码中要求为Iterator[(K,V)]:
猜测网上产生这种错误的原因可能是受重载函数的影响:
本节摘自:https://endymecy.gitbooks.io/spark-programming-guide-zh-cn/content/spark-streaming/basic-concepts/output-operations-on-DStreams.html
输出操作允许DStream的操作推到如数据库、文件系统等外部系统中。因为输出操作实际上是允许外部系统消费转换后的数据,它们触发的实际操作是DStream转换。目前,定义了下面几种输出操作:
Output Operation | Meaning |
---|---|
print() | 在DStream的每个批数据中打印前10条元素,这个操作在开发和调试中都非常有用。在Python API中调用pprint() 。 |
saveAsObjectFiles(prefix, [suffix]) | 保存DStream的内容为一个序列化的文件SequenceFile 。每一个批间隔的文件的文件名基于prefix 和suffix 生成。"prefix-TIME_IN_MS[.suffix]",在Python API中不可用。 |
saveAsTextFiles(prefix, [suffix]) | 保存DStream的内容为一个文本文件。每一个批间隔的文件的文件名基于prefix 和suffix 生成。"prefix-TIME_IN_MS[.suffix]" |
saveAsHadoopFiles(prefix, [suffix]) | 保存DStream的内容为一个hadoop文件。每一个批间隔的文件的文件名基于prefix 和suffix 生成。"prefix-TIME_IN_MS[.suffix]",在Python API中不可用。 |
foreachRDD(func) | 在从流中生成的每个RDD上应用函数func 的最通用的输出操作。这个函数应该推送每个RDD的数据到外部系统,例如保存RDD到文件或者通过网络写到数据库中。需要注意的是,func 函数在驱动程序中执行,并且通常都有RDD action在里面推动RDD流的计算。 |
dstream.foreachRDD是一个强大的原语,发送数据到外部系统中。然而,明白怎样正确地、有效地用这个原语是非常重要的。下面几点介绍了如何避免一般错误。
dstream.foreachRDD(rdd => { val connection = createNewConnection() // executed at the driver rdd.foreach(record => { connection.send(record) // executed at the worker }) })
这是不正确的,因为这需要先序列化连接对象,然后将它从driver发送到worker中。这样的连接对象在机器之间不能传送。它可能表现为序列化错误(连接对象不可序列化)或者初始化错误(连接对象应该 在worker中初始化)等等。正确的解决办法是在worker中创建连接对象。
dstream.foreachRDD(rdd => { rdd.foreach(record => { val connection = createNewConnection() connection.send(record) connection.close() }) })
通常,创建一个连接对象有资源和时间的开支。因此,为每个记录创建和销毁连接对象会导致非常高的开支,明显的减少系统的整体吞吐量。一个更好的解决办法是利用rdd.foreachPartition
方法。 为RDD的partition创建一个连接对象,用这个两件对象发送partition中的所有记录。
dstream.foreachRDD(rdd => { rdd.foreachPartition(partitionOfRecords => { val connection = createNewConnection() partitionOfRecords.foreach(record => connection.send(record)) connection.close() }) })
这就将连接对象的创建开销分摊到了partition的所有记录上了。
dstream.foreachRDD(rdd => { rdd.foreachPartition(partitionOfRecords => { // ConnectionPool is a static, lazily initialized pool of connections val connection = ConnectionPool.getConnection() partitionOfRecords.foreach(record => connection.send(record)) ConnectionPool.returnConnection(connection) // return to the pool for future reuse }) })
需要注意的是,池中的连接对象应该根据需要延迟创建,并且在空闲一段时间后自动超时。这样就获取了最有效的方式发生数据到外部系统。
其它需要注意的地方:
dstream.foreachRDD()
,但是没有任何RDD action操作在dstream.foreachRDD()
里面,那么什么也不会执行。系统仅仅会接收输入,然后丢弃它们。
参考资料
https://www.ibm.com/developerworks/cn/opensource/os-cn-spark-practice2/index.html
https://github.com/apache/spark/blob/master/streaming/src/main/scala/org/apache/spark/streaming/dstream/DStream.scala