Spark Streaming(一)

本人刚开始入门学习Spark,打算先将Spark文档看一遍,顺便做点笔记,就进行一些翻译和记录。由于本人只会python,所以翻译都是以python部分代码进行。以下并非完全100%官网翻译,更多是个人理解+笔记+部分个人认为重要的内容的翻译,新手作品,请各位大神多多指正。
原文地址:http://spark.apache.org/docs/latest/streaming-programming-guide.html

Overview

Spark Streaming是核心Spark API对于实时数据流处理的扩展功能。数据可以从kafka,flume,kinesis或者tcp socket导入,可以通过map,reduce,join,window等高级函数进行处理。处理好的结果能够保存到文件系统,数据库或者在仪表盘进行实时展示。

工作流程如下图。Spark Streaming接收到实时输入数据,并且将数据分割为批(batches),由Spark引擎对其进行处理后,生成最终的流结果集(以批为单位)

Spark Streaming提供了高层次抽象discretized stream 或 DStream,其表示了连续的流数据。Dstream可以通过kafka,flume,kinesis或者由其他Dstream为创建源。内部来说,Dstream实际为RDD组成的序列。

A Quick Example

以统计从tcp端口接收到的文字数量为例:

SparkContext是所有流功能的入口点

from pyspark import SparkContext
from pyspark.streaming import StreamingContext

# 创建一个有2个工作线程的 StreamingContext 对象,批间隔为1s
sc = SparkContext("local[2]", "NetworkWordCount")
ssc = StreamingContext(sc, 1)

通过这个context创建一个Dstream

# Create a DStream that will connect to hostname:port, like localhost:9999
lines = ssc.socketTextStream("localhost", 9999)

lines表示将从tcp获取的数据流,

# Split each line into words
words = lines.flatMap(lambda line: line.split(" "))

flatmap 是1对多的Dstream操作,其创建了一个新的Dstream。在这个例子中,每一行会被分割成多个单词,新的Dstream即为单词流。

# Count each word in each batch
pairs = words.map(lambda word: (word, 1))
wordCounts = pairs.reduceByKey(lambda x, y: x + y)

# Print the first ten elements of each RDD generated in this DStream to the console
wordCounts.pprint()

单词DStream被1对1映射为(word,1)键值对,然后由ReduceByKey处理。最后由ppring()打印每秒生成的结果。

注意上述并没有实际执行计算,如果执行计算和处理,就得调用下面语句

ssc.start()             # Start the computation
ssc.awaitTermination()  # Wait for the computation to terminate

另外还需要执行

nc -lk 9999

Basic Concepts

Linking

与Spark类似,Spark Streaming可以通过Maven来构建。但需要添加相应依赖。对于 Kafka, Flume, 和 Kinesis等在Core APi中没有原生支持的数据源,需要将spark-streaming-xyz_2.11添加到依赖中。

Initializing StreamingContext

from pyspark import SparkContext
from pyspark.streaming import StreamingContext

sc = SparkContext(master, appName)
ssc = StreamingContext(sc, 1)
  1. 首先要创建SparkContext对象,然后创建StreamingContext对象。
  2. 定义数据源,创建Dstream
  3. 通过转换和输出操作定义数据源计算过程
  4. 通过streamingContext.awaitTermination()等待过程停止,也可以通过streamingContext.stop()停止

需要注意的点:

  • 一旦context开始载入工作,就不能新增其他流计算
  • 一旦context停止工作,就不能重新启动
  • 在同一时间,有且只有一个StreamingContext在一个JVM中激活
  • stop() 将停止StreamingContext和SparkContext,如果只想要停止StreamingContext,可以再 stop()中设置参数stopSparkContext为falese
  • SparkContext可以通过创建多个StreamingContexts被重复利用,条件是上一个StreamingContext已停止。

Discretized Streams (DStreams)

DStream是有Spark Streaming的基本抽象对象,其代表了连续的数据流。内部DStream是有一系列连续的RDDs组成,每个RDD都包含着一定时间间隔中的数据。

所有应用到Dstream上的操作最终应用到的是RDDs。在上面例子中,flatmap被应用到每个RDD中,再形成了单词Dstream的RDDs。

Input DStreams and Receivers

输入DStream表示的是输入数据流,在例子中,输入流是来自netcat服务器的数据。每一个输入DStream(除了文件流)都必须有对应的receiver对象,它的作用是从数据源接受数据并且储存在Spark的内存中等待处理。

有两种内置的数据源类型

  • 基本类型/Basic Sources:对于StreamingContext API直接可用的数据源,例如文件系统,socket连接。
  • 高级类型/Advanced Sources:kafka,flume,kinesis等数据源,通过附加的依赖可被输入到Spark中

如果需要同时接受多个数据流,可以创建多个输入DStream,这就意味着需要创建多个receiver。值得注意的是Spark worker/executor是一个长期运行的任务,所以其将占据其中一个分配给Spark Streaming的cpu核心。所以流处理应用必须要有足够核心(本地的话也可以是线程)来处理接受和数据和运行receiver。

需要注意的点:

  • 本地运行spark流处理,不要用’local’或者’local[1]’,上述意思是只分配一个线程。那么这个线程可能会被receiver占用,那么就没有线程来对数据进行处理。所以本地运行n必须大于receiver的数据。

  • 将上述逻辑扩展到集群,分配给流处理的核心数量必须大于receivers的数量。

Basic Sources

除了上述例子的socket连接,文件也属于基本类型。

File Streams

可以通过StreamingContext.fileStream[KeyClass, ValueClass, InputFormatClass]来创建DStream来读取和HDFS API兼容的任何文件系统文件(例如HDFS,S3,NFS等)

文件流不需要运行receiver

python api不支持文件流,只支持文档流

streamingContext.textFileStream(dataDirectory)
How Directories are Monitored

Spark流将监控指定的文件夹并处理其中的所有文件。

  • 可以监控简单的文件夹,例如’hdfs://namenode:8040/logs/’
  • 可以使用通配符,例如’hdfs://namenode:8040/logs/2017/*’
  • 文件数据格式必须一致
  • 文件是基于修改时间而非创建时间来评判是否需要处理
  • 一旦文件被处理过,即使在当前window的文件的修改也不会触发再次读取处理。
  • 文件夹文件越多,就需要更长时间来扫描更改,即使没有文件被更改也是如此。
  • 如果采用通配符,例如’hdfs://namenode:8040/logs/2016-*’,符合路径的整个文件夹也会被纳入监控。但只有修改时间在当前window中的文件会被处理。
  • 调用FileSystem.setTimes()来修改时间是让window重新处理文件的办法,即使该文件没有被修改。
Using Object Stores as a source of data

完整的文件系统例如HDFS趋向于输出流被创建以后马上设置文件的修改时间。即使文件被打开,特别是更新还没有完成的情况下,这部分内容可能会被忽略。

为了保证修改能够被window正确读取,可以将文件拷贝到非监控目录,在输出流关闭后马上将文件mv到原目录。

与此相反,例如S3等对象存储的mv操作非常慢,因为数据是真实发生拷贝移动的。而且,mv对象会将mv操作时间作为其修改时间,所以也可能不会符合window的范围。

Streams based on Custom Receivers

用户可以自定义receiver来创建DStream

Queue of RDDs as a Stream

如果用于测试SPark流处理,可以通过streamingContext.queueStream创建队列RDD,每个队列中的RDD将会被当成DStream中的一个批单位处理。

Advanced Sources

这类数据源需要和对接外部非Spark库,某些库需要复杂的依赖才能运行。因此,为了减少复杂度,从这些源创建DStream的功能被分割到独立的库中,可按需连接。

上述数据源在SPark shell中是不可用的,因为在shell中无法测试基于上述数据源的应用。如果想要在spark shell中运行,可以下载Maven的jar和依赖。

Custom Sources

该部分python api不支持。

输入流可以自定义,只需要自己定义一个receiver即可。

Receiver Reliability

kafka和flume支持ack确认

Reliable Receiver - 当数据被接受并且在spark中以多副本存放后,receiver会发送ack到数据源
Unreliable Receiver - receiver不发送ack到数据源。用于不支持ack确认的数据源,或者不需要ack确认增加复杂性的场合。

Transformations on DStreams

转换用法
map(func)将函数func作用在DStream每个元素上,并返回一个新的DStream
flatMap(func)类似于map,但每个元素可以映射到0-N个输出中。
filter(func)将函数func作用在每个元素上,只返回由func判断后返回ture的那些元素组成的DStream
repartition(numPartitions)通过调整分区数量改变DStream的处理并行度
union(otherStream)返回组合源DStream和其他DStream的组合而成的新的DStream
count()返回一个包含多个单元素RDD的DStream,RDD中包含了每个源RDD中元素数量的个数
reduce(func)通过func(接受2个参数并返回1个值)对源DStream每个RDD元素的聚合,返回一个包含多个单元素RDD的DStream。函数应该具备无序性和相关性
countByValue()作用在DStream的元素上,返回一个类型为(K,long)的键值对,其中值为源DStream中每个RDD的key出现过的次数。
reduceByKey(func, [numTasks])作用在类型为(K,V)的DStream上,返回类型为(K,V)的新的DStream,值为通过func对每个key的聚合以后的值。可以通过numTasks制定任务数量
join(otherStream, [numTasks])作用在两个类型为(K, V) 和 (K, W)的DStream的键值对上,返回类型为(K, (V, W))的键值对。
cogroup(otherStream, [numTasks])作用在(K, V) 和 (K, W)的DStream键值对上,返回一个以元组(K, Seq[V], Seq[W])类型的DStream。
transform(func)函数将作用在源DStream的每个RDD上,并返回一个新的DStream,这样就可以在DStream上应用RDD的全部类型操作
updateStateByKey(func)返回一个新’状态‘的RDD,通过函数对每个key的状态和值进行更新。

UpdateStateByKey Operation

updateStateByKey可以在持续计算结果更新中维护一个状态以及值。

  1. 定制状态,状态可以为任意数据类型
  2. 定义状态更新函数,定义函数如何更新当前状态和从输入流计算新的值。

对于每个batch单位,spark会将func应用到所有已存在的键值对上,而不理会是否为新数据。如果func返回None则k-v对将被清除(有误,例子并非如此)

例如想要持续看到输入流中每个词的个数,这里running count就是状态,值为一个整数。

def updateFunction(newValues, runningCount):
    if runningCount is None:
        runningCount = 0
    return sum(newValues, runningCount)  # add the new values with the previous running count to get the new count
runningCounts = pairs.updateStateByKey(updateFunction)

func将对每个词调用,每秒更新1次newValue值到runningcount中。

updateStateByKey需要配置checkpoint目录,例如ssc.checkpoint(‘/temp’)

Transform Operation

转换操作(包括其衍生操作transformWith)能够将任意将RDD-RDD函数应用到DStream中,这就可以将任何没有在DStream API直接支持的RDD操作应用到DStream中。例如将每个batch与另一个数据集进行join操作并没有在DStream直接支持,通过Transform操作后即可利用RDD的join实现。例如,可以通过将spam信息实时join到输入数据流中实现实时数据清理。

spamInfoRDD = sc.pickleFile(...)  # RDD containing spam information

# join data stream with spam information to do data cleaning
cleanedDStream = wordCounts.transform(lambda rdd: rdd.join(spamInfoRDD).filter(...))

注意是在每个batch中应用该函数,所以实现可以随着时间改变的RDD操作,例如分区数更改,广播变量等,可以随着batch不同而不同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值