概述
一般流式计算会与批量计算相比较。在流式计算模型中,输入时持续的,可以认为在时间上是无界的,也就意味着,永远拿不到全量数据去做计算。同时,计算结果是持续输出的,也即计算结果在时间上也是无界的。流式计算一般对实时性要求较高,同时一般是先定义目标计算,然后数据到来之后将计算逻辑应用于数据。同时为了提高计算效率,往往尽可能采用增量计算代替全量计算。批量处理模型中,一般先有全量数据集,然后定义计算逻辑,并将计算应用于全量数据。特点是全量计算,并且计算结果一次性全量输出。
批处理VS流处理区别
目前主流流处理框架:Kafka Streaming、Storm(JStrom)、Spark Streaming、Flink(BLink)
- Kafka Streaming:是一套基于Kafka-Streaming库的一套流计算工具jar包,具有入门门槛低,简单容易集成等特点。
- Apache Strom:一款纯粹的流计算引擎,能够达到每秒钟百万级别数据的低延迟处理框架。
- Spark Streaming:是构建在Spark批处理之上一款流处理框架。与批处理不同的是,流处理计算的数据是无界数据流,输出也是持续的。Spark Streaming底层将Spark RDD Batch拆分成Macro RDD Batch实现类似流处理的功能。因此Spark Streaming在微观上依旧是批处理框架。
- Flink DataStream:在实时性和应用性上以及性能都有很大的提升,是目前为止最火热的流计算引擎。
快速入门
- 导入依赖
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.4.5</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>2.4.5</version>
</dependency>
- 编写Driver
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
object DStreamWordCount {
def main(args: Array[String]): Unit = {
//1.创建StreamContext
val conf = new SparkConf().setAppName("wordcount").setMaster("local[*]")
val ssc = new StreamingContext(conf,Seconds(1))
ssc.sparkContext.setLogLevel("FATAL")
//2.创建持续输入DStream
val lineStream = ssc.socketTextStream("train",9999)
//3.对lineStream做算子转换
lineStream.flatMap(_.split(" "))
.map((_,1))
.reduceByKey(_+_)
.print()//打印输出console
//4.启动计算
ssc.start()
//5.等待关闭
ssc.awaitTermination()
}
}
- 使用mvn package进行打包
<build>
<plugins>
<!--scala编译插件-->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>4.0.1</version>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<!--创建fatjar插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
<!--编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
- 下载nc组件
[root@train ~]# yum -y install nmap-ncat
- 启动nc服务
[root@train ~]# nc -lk 9999
- 启动服务
[root@train spark-2.4.5]# ./bin/spark-submit --master spark://train:7077 --name DStreamWordCount --deploy-mode client --class com.baizhi.sparkstreaming.DStreamWordCount --total-executor-cores 6 /root/spark-streaming-1.0-SNAPSHOT.jar
- 用户可以访问webUI
Discretized Streams
Discretized Streams或DStream是Spark Streaming提供的基本抽象。它表示连续的数据流,可以是从源接受的输入数据流,也可以是通过转换输入流生成的已处理数据流。在内部,DStream由一系列连续的RDD表示,这是Spark对不可变分布式数据集的抽象。DStream中的每个RDD都包含来自特定时间间隔的数据,如下图所示。
应用于DStream的任何操作都转换为底层RDD上的操作。例如,在先前快速入门
示例中,flatMap操作应用于行DStream中的每个RDD以生成单词DStream的RDD。
注意:通过对DStream底层运行机制的了解,在设计StreamingContext的时候要求设置的Seconds()
间隔要略大于微批的计算时间。这样才可以有效的避免数据在Spark的内存中产生积压。
DStream&Receivers
每个输入DStream(file stream除外,稍后讨论)都与Receiver对象相关联,该对象从源接受数据并将其存储在Spark的内存中进行处理。Spark Streaming提供了两类内建的输入源,用于接受外围系统数据:
内建输入源
- Basic sources-Spark中的StreamContext的API可以直接获取的数据源,例如:fileStream(读文件)、socket(测试)
socketTextStream
val lineStream = ssc.socketTextStream("train",9999)
File Streams
val lineStream = ssc.textFileStream("hdfs://train:9000/demo/words")
或者
val lines = ssc.fileStream("hdfs://train:9000/demo/word")
根据时间监测
hdfs://train:9000/demo/word
是否有新文件
产生,如果有新文件产生,系统就自动读取该文件。并不会监控文件内容的变化。提示:在测试的时候,一定要注意同步时间。
- Queue of RDDs(测试)
var queueRDDs = new mutable.Queue[RDD[String]]()
//产生测试数据
new Thread(new Runnable {
override def run(): Unit = {
while (true){
queueRDDs += ssc.sparkContext.makeRDD(List("this is a demo","hello hello"))
Thread.sleep(100)
}
}
}).start()
//2.创建持续输入DStream
val lineStream = ssc.queueStream(queueRDDs)
高级数据源
- Advanced sources:并不是Spark自带的源,例如:Kafka、Flume、Kinesis等,这些一般都需要第三方支持
Custom Receiver(自定义)
class CustomReceiver(values:List[String])
extends Receiver[String](StorageLevel.MEMORY_ONLY) with Logging{
override def onStart(): Unit = {
new Thread(new Runnable {
override def run(): Unit = receive()
}).start()
}
override def onStop(): Unit = {}
//接受来自外围系统的数据
private def receive() {
try {
while (!isStarted()){
Thread.sleep(500)
val line = values(new Random().nextInt(values.length))
store(line)
}
restart("Trying to restart again")
} catch {
case t: Throwable =>
// restart if there is any other error
restart("Error receiving data", t)
}
}
}
val arrays = List("this is a demo","good good","study come on")
//2.创建持续输入DStream
val lineStream = ssc.receiverStream(new CustomReceiver(arrays))
Spark对接Kafka
参考资料:http://spark.apache.org/docs/latest/streaming-kafka-0-10-integration.html
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
<version>2.4.5</version>
</dependency>
- 准备
开启zookeeper
启动kafka
[root@train kafka_2.11-2.2.0]# ./bin/kafka-server-start.sh -daemon config/server.properties
创建一个topic01
[root@train kafka_2.11-2.2.0]# ./bin/kafka-topics.sh --bootstrap-server train:9092 --create --topic topic01 --partitions 30 --replication-factor 1
//1.创建StreamContext
val conf = new SparkConf().setAppName("wordcount").setMaster("local[*]")
val ssc = new StreamingContext(conf,Seconds(5))
ssc.sparkContext.setLogLevel("FATAL")
val kafkaParams = Map[String, Object](
"bootstrap.servers" -> "train:9092", //kafka链接配置参数
"key.deserializer" -> classOf[StringDeserializer], //消费者--key的反序列化
"value.deserializer" -> classOf[StringDeserializer], //消费者--value的反序列化
"group.id" -> "g1",//设置消费组
"auto.offset.reset" -> "latest",//偏移量策略
"enable.auto.commit" -> (false: java.lang.Boolean) //是否自动提交偏移量
)
val topics = Array("topic01") //订阅topic
val stream = KafkaUtils.createDirectStream[String, String](
ssc, //StreamContext -- 流计算环境
LocationStrategies.PreferConsistent, //位置策略配置
ConsumerStrategies.Subscribe[String,String](topics,kafkaParams) //检查点重新启动之后也可以获得正确配置
)
stream.map(result=>result.value())
.flatMap(_.split(" "))
.map(t=>(t,1))
.reduceByKey(_+_)
.print()
//4.启动计算
ssc.start()
//5.等待关闭
ssc.awaitTermination()
生产者
[root@train kafka_2.11-2.2.0]# ./bin/kafka-console-producer.sh --broker-list train:9092 --topic topic01