1、SparkStreaming的概述
1.1、什么是流式计算
1、流式计算就是对数据流进行处理,是实时计算
2、数据流值的是动态的数据,是不断产生的,没有边界,源源不断
3、流式计算中的计算逻辑不止计算一次,是要一致循环计算的(计算不能终止,除非停止作业)
流式计算又分为准实时和实时
准实时:是介于实时和离线之间,每一次处理的数据要比实时的多,比离线的少很多,微批处理
实时:指的是一条记录就(一个事件event)启动一次计算
常见的流式计算框架
storm:第一代流式处理框架,每生成一条记录就提交一次作业,实时流处理,延迟低
sparkstreaming:第二代流式处理框架,短时间内生成mirco-batch,提交一次作业。准实时,延迟略高。
filnk-datastream(blink):第三代的流式处理框架,每生成一条记录,提交一次作业,实时,延迟低。
1.2、什么是离线计算
1、离线计算是对批量的静态数据进行批处理
2、离线计算的数据是静态的,有边界的
3、离线计算中的计算逻辑只计算一次
常见的离线就算框架(批处理,一批数据进行一次计算,延迟高)
mapreduce
hive
sparksql
sparkcore
flinkdataset
1.3、sparkstreaming简介
是一个基于Spark Core之上的实时计算框架,可以从很多数据源消费数据并对数据进行处理.Spark Streaming 是Spark核心API的一个扩展,可以实现高吞吐量的、具备容错机制的实时流数据的处理。支持从多种数据源获取数据,包括Kafk、Flume、Twitter、ZeroMQ、Kinesis 以及TCP sockets,从数据源获取数据之后,可以使用诸如map、reduce、join和window等高级函数进行复杂算法的处理。最后还可以将处理结果存储到文件系统,数据库和现场仪表盘。在“One Stack rule them all”的基础上,还可以使用Spark的其他子框架,如集群学习、图计算等,对流数据进行处理。
Spark Streaming处理的数据流图:

Spark的各个子框架,都是基于核心Spark的,Spark Streaming在内部的处理机制是,接收实时流的数据,并根据一定的时间间隔拆分成一批批的数据,然后通过Spark Engine处理这些批数据,最终得到处理后的一批批结果数据。
对应的批数据,在Spark内核对应一个RDD实例,因此,对应流数据的DStream可以看成是一组RDDs,即RDD的一个序列。通俗点理解的话,在流数据分成一批一批后,通过一个先进先出的队列,然后 Spark Engine从该队列中依次取出一个个批数据,把批数据封装成一个RDD,然后进行处理,这是一个典型的生产者消费者模型,对应的就有生产者消费者模型的问题,即如何协调生产速率和消费速率。
1.3.1、术语定义
离散流(discretized stream)或DStream:
本质上就是一系列连续的RDD,DStream其实就是对RDD的封装DStream可以任务是一个RDD的工厂,该DStream里面生产都是相同业务逻辑的RDD,只不过是RDD里面要读取数据的不相同
他是sparkStreaming中的一个最基本的抽象,代表了一下列连续的数据流,本质上是一系列连续的RDD,你对DStream进行操作,就是对RDD进行操作
DStream每隔一段时间生成一个RDD,你对DStream进行操作,本质上是对里面的对应时间的RDD进行操作
DSteam和DStream之间存在依赖关系,在一个固定的时间点,对个存在依赖关系的DSrteam对应的RDD也存在依赖关系,
每个一个固定的时间,其实生产了一个小的DAG,周期性的将生成的小DAG提交到集群中运行
DStream图例:

批数据(batch data):这是化整为零的第一步,将实时流数据以时间片为单位进行分批,将流处理转化为时间片数据的批处理。随着持续时间的推移,这些处理结果就形成了对应的结果数据流了。
时间片或批处理时间间隔( batch interval):这是人为地对流数据进行定量的标准,以时间片作为我们拆分流数据的依据。一个时间片的数据对应一个RDD实例。
窗口长度(window length):一个窗口覆盖的流数据的时间长度。必须是批处理时间间隔的倍数,
滑动时间间隔:前一个窗口到后一个窗口所经过的时间长度。必须是批处理时间间隔的倍数
Input DStream :一个input DStream是一个特殊的DStream,将Spark Streaming连接到一个外部数据源来读取数据。
1.3.2、 Storm和Spark Streaming比较
处理模型以及延迟
虽然两框架都提供了可扩展性(scalability)和可容错性(fault tolerance),但是它们的处理模型从根本上说是不一样的。Storm可以实现亚秒级时延的处理,而每次只处理一条event,而Spark Streaming可以在一个短暂的时间窗口里面处理多条(batches)Event。所以说Storm可以实现亚秒级时延的处理,而Spark Streaming则有一定的时延。
容错和数据保证
然而两者的代价都是容错时候的数据保证,Spark Streaming的容错为有状态的计算提供了更好的支持。在Storm中,每条记录在系统的移动过程中都需要被标记跟踪,所以Storm只能保证每条记录最少被处理一次,但是允许从错误状态恢复时被处理多次。这就意味着可变更的状态可能被更新两次从而导致结果不正确。
任一方面,Spark Streaming仅仅需要在批处理级别对记录进行追踪,所以他能保证每个批处理记录仅仅被处理一次,即使是node节点挂掉。虽然说Storm的 Trident library可以保证一条记录被处理一次,但是它依赖于事务更新状态,而这个过程是很慢的,并且需要由用户去实现。
实现和编程API
Storm主要是由Clojure语言实现,Spark Streaming是由Scala实现。如果你想看看这两个框架是如何实现的或者你想自定义一些东西你就得记住这一点。Storm是由BackType和Twitter开发,而Spark Streaming是在UC Berkeley开发的。
Storm提供了Java API,同时也支持其他语言的API。 Spark Streaming支持Scala和Java语言(其实也支持Python)。
批处理框架集成
Spark Streaming的一个很棒的特性就是它是在Spark框架上运行的。这样你就可以想使用其他批处理代码一样来写Spark Streaming程序,或者是在Spark中交互查询。这就减少了单独编写流批量处理程序和历史数据处理程序。
生产支持
Storm已经出现好多年了,而且自从2011年开始就在Twitter内部生产环境中使用,还有其他一些公司。而Spark Streaming是一个新的项目,并且在2013年仅仅被Sharethrough使用(据作者了解)。
Storm是 Hortonworks Hadoop数据平台中流处理的解决方案,而Spark Streaming出现在 MapR的分布式平台和Cloudera的企业数据平台中。除此之外,Databricks是为Spark提供技术支持的公司,包括了Spark Streaming。
虽然说两者都可以在各自的集群框架中运行,但是Storm可以在Mesos上运行, 而Spark Streaming可以在YARN和Mesos上运行。

1.4、运行原理
1.4.1、 Streaming架构
SparkStreaming是一个对实时数据流进行高通量、容错处理的流式处理系统,可以对多种数据源(如Kafka、Flume、Twitter、Zero和TCP 套接字)进行类似Map、Reduce和Join等复杂操作,并将结果保存到外部文件系统、数据库或应用到实时仪表盘。
计算流程:Spark Streaming是将流式计算分解成一系列短小的批处理作业。这里的批处理引擎是Spark Core,也就是把Spark Streaming的输入数据按照batch size(如1秒)分成一段一段的数据(Discretized Stream),每一段数据都转换成Spark中的RDD(Resilient Distributed Dataset),然后将Spark Streaming中对DStream的Transformation操作变为针对Spark中对RDD的Transformation操作,将RDD经过操作变成中间结果保存在内存中。整个流式计算根据业务的需求可以对中间的结果进行叠加或者存储到外部设备。下图显示了Spark Streaming的整个流程。

2、案例
使用SparkStreaming需要导入如下依赖
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>2.2.2</version>
</dependency>
2.1、入口类StreamingContext
SparkStreaming中的入口类,称之为StreamingContext,但是底层还是得需要依赖SparkContext。
object _01SparkStreamingWordCountOps {
def main(args: Array[String]): Unit = {
/*
StreamingContext的初始化,需要至少两个参数,SparkConf和BatchDuration
SparkConf不用多说
batchDuration:提交两次作业之间的时间间隔,每次会提交一个DStream,将数据转化batch--->RDD
所以说:sparkStreaming的计算,就是每隔多长时间计算一次数据
*/
val conf = new SparkConf()
.setAppName("SparkStreamingWordCount")
.setMaster("local[*]")
val duration = Seconds(2)
val ssc = new StreamingContext(conf, duration)
//业务
//为了执行的流式计算,必须要调用start来启动
ssc.start()
//为了不至于start启动程序结束,必须要调用awaitTermination方法等待程序业务完成之后调用stop方法结束程序,或者异常
ssc.awaitTermination()
}
}
2.2、单词统计案例
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Duration, Durations, StreamingContext}
/**
* sparkstreaming是一个准实时的计算框架
* 使用其来接收nc(netcat)客户端发送过来的数据,进行单词单词统计
*
* 注意:要先启动nc -lk qianfeng01 10086
*/
object _01SparkStreamingDemo01 {
def main(args: Array[String]): Unit = {
//获取一个配置对象
//如果是本地模式,那么最少需要两个线程。一个线程负责接收数据,另一个负责计算
val conf: SparkConf = new SparkConf().setAppName("test1").setMaster("local[2]")
/*
* 获取一个Duration对象,用于指定两次作业的间隔时间
* Durations.seconds()
* Durations.minutes()
* Durations.milliseconds()
* Seconds()
* Minutes()
* Milliseconds()
*/
val duration: Duration = Durations.seconds(10)
//获取SparkStreaming的上下文对象StreamingContext
val context = new StreamingContext(conf, duration)
//读取TCP协议,也就是NC发送的数据
val dStream: ReceiverInputDStream[String] = context.socketTextStream("datanode01", 10086)
//使用算子进行计算,返回新的DStream
val value: DStream[(String, Int)] = dStream.map((_, 1)).reduceByKey(_ + _)
//打印
value.print()
//启动计算程序
context.start()
//等待终止,避免没接收到数据就终止程序的情况
context.awaitTermination()
}
}
3、SparkStreaming和HDFS的整合
3.1、SprakStreaming读取本地文件
注意: SparkStreaming读取的本地文件必须是IO流现产生的新文件,不能识别旧文件,也不能识别拷贝的文件
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
* SparkSteaming处理本地文件,只能是IO流产生的新文件,并且只读一次,也就是说本地文件继续追加时,是不能识别的
*/
object _02SparStreamingReadLocalFile {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("test1").setMaster("local[2]")
//获取上下文对象
val ssc = new StreamingContext(conf, Seconds(5))
//读取本地文件
val dSteam1: DStream[String] = ssc.textFileStream("D:/data")
dSteam1.print()
//启动程序
ssc.start()
//防止没有读到数据,就终止计算的情况
ssc.awaitTermination()
}
}
下面是java写的一个输出流:
package com.qf.sparkstreaming;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class _03JavaIOOP {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("D:/data/new.txt",true);
int count = 0;
while(count<100){
fos.write(("hello"+Math.random()).getBytes());
Thread.sleep(100);
count++;
}
fos.close();
}
}
3.2、Spark Streaming读取HDFS上的文件
注意: SparkStreaming读取的HDFS上的文件可以是put上去的,也可以是copyFromLocal但是不能是moveFromLocal的文件
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
* SparkSteaming处理本地文件,只能是IO流产生的新文件,并且只读一次,也就是说本地文件继续追加时,是不能识别的
*/
object _04SparStreamingReadHDFSFile {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("test1").setMaster("local[2]")
//获取上下文对象
val ssc = new StreamingContext(conf, Seconds(5))
//读取本地文件
val dSteam1: DStream[String] = ssc.textFileStream("hdfs://datanode01:8020/streaming/")
dSteam1.print()
//启动程序
ssc.start()
//防止没有读到数据,就终止计算的情况
ssc.awaitTermination()
}
}
4、Spark Streaming与kafka的整合
需要导入如下maven依赖
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-streaming-kafka-0-10 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
<version>2.2.3</version>
</dependency>
案例:
package com.qf.sparkstreaming
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, KafkaUtils, LocationStrategies}
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
* SparkStreaming与Kafka的整合,用于消费Kafka里的信息
* 使用的整合包是0-10. 使用里面的Direct直连方式。 而0-8里除了direct还有一个reciver方法
*/
object _05SparkStreamingKafkaDemo {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("test1").setMaster("local[2]")
.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
//获取上下文对象
val ssc = new StreamingContext(conf, Seconds(10))
//设置消费者的属性信息
val params: Map[String, String] = Map[String, String](
"bootstrap.servers" -> "datanode01:9092,datanode01:9092,datanode01:9092",
"group.id" -> "test1",
"auto.offset.reset" -> "latest",
"key.deserializer" -> "org.apache.kafka.common.serialization.IntegerDeserializer",
"value.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer"
)
//使用整合包里的工具类,调用直连方法
val dStream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream(ssc,
LocationStrategies.PreferConsistent, //指定消费策略:PreferConsistent, SparkStreaming会为kafka主题中的每一个分区分配一个计算
ConsumerStrategies.Subscribe[String,String](Array("pet").toSet, params)
)
//打印数据
dStream.map(_.value()).print()
//启动
ssc.start()
ssc.awaitTermination()
}
}

被折叠的 条评论
为什么被折叠?



