1. 流式计算是什么
1.1.离线批处理(batch calculation)
所谓离线批处理,这里面有两个概念,一个是离线,还是一个是批处理。
先看批处理,说白了处理的是一批数据,只不过这里的一批,数据量往往相对比较大,比如100G,500G,1T等等;离线指的是,数据是静态,或者说数据不变。所以二者合一,所谓离线批处理,就是对静态的,不变的数据集进行处理。
有哪些特点呢?
- 处理的数据是静态的,不变的;
- 处理的数据是有界限的(Bounded);
- 计算过程一定会结束;
- 计算是一锤子的买卖;
- 计算过程是非常缓慢的。
1.2.实时流计算(real time stream calculation)
所谓的实时流计算,是和离线批处理相对应的,所谓实时流,就是向水流一样,川流不息,不间断,实时的有数据产生。对这些数据的处理,就不能再像离线批处理那样?
有哪些特点?
- 处理的数据是动态,实时产生的;
- 处理的数据是无界的(UnBounded),但是可以衡量某个时间段内的体积;
- 计算是持续不断的,除非外力终止,否则持续不断计算;
- 不会只计算一次;
- 计算的过程必须非常的快速。
2.Spark Streaming实时案例实战
2.1. 构建Streaming模块
项目模块的pom依赖,结构如下:
spark-parent.pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.desheng.bigdata</groupId>
<artifactId>spark-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spark-core</module>
<module>spark-common</module>
<module>spark-sql</module>
<module>spark-streaming</module>
</modules>
<packaging>pom</packaging>
<properties>
<spark.version>2.2.2</spark.version>
<scala.version>2.11.8</scala.version>
<hadoop.version>2.7.6</hadoop.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 所有的子模块都需要(继承)的依赖-->
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
</dependencies>
</project>
spark-streaming.pom
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>spark-parent</artifactId>
<groupId>com.desheng.bigdata</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.desheng.bigdata</groupId>
<artifactId>spark-streaming</artifactId>
<version>1.0-SNAPSHOT</version>
<inceptionYear>2008</inceptionYear>
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<scalaVersion>${scala.version}</scalaVersion>
<args>
<arg>-target:jvm-1.5</arg>
</args>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<configuration>
<downloadSources>true</downloadSources>
<buildcommands>
<buildcommand>ch.epfl.lamp.sdt.core.scalabuilder</buildcommand>
</buildcommands>
<additionalProjectnatures>
<projectnature>ch.epfl.lamp.sdt.core.scalanature</projectnature>
</additionalProjectnatures>
<classpathContainers>
<classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
<classpathContainer>ch.epfl.lamp.sdt.launching.SCALA_CONTAINER</classpathContainer>
</classpathContainers>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<configuration>
<scalaVersion>${scala.version}</scalaVersion>
</configuration>
</plugin>
</plugins>
</reporting>
</project>
2.2. streaming入门案例
/**
* SparkStreaming 基于网络端口产生实时数据,进行统计的入门案例
*
* SparkStreaming编程的入口类:StreamingContext
*/
object NetWorkStreamingOps {
def main(args: Array[String]): Unit = {
if(args == null || args.length < 3) {
println(
"""
|Parameter Errors! Usage: <batchInterval> <host> <port>
""".stripMargin)
System.exit(-1)
}
val Array(batchInterval, host, port) = args
val conf = new SparkConf()
.setAppName("NetWorkStreamingOps")
.setMaster("local[*]")
val ssc = new StreamingContext(conf, Seconds(batchInterval.toLong))
/*
接入外部网络产生的数据,转化成RDD--->DStream
摄入算子大多数都有持久化级别,大多数默认的级别就是StorageLevel.MEMORY_AND_DISK_SER_2
*/
val lines:ReceiverInputDStream[String] = ssc.socketTextStream(host, port.toInt)
val pairs:DStream[(String, Int)] = lines.flatMap(_.split("\\s+")).map((_, 1))
val ret = pairs.reduceByKey(_+_)
//打印结果到控制台
ret.print()
ssc.start()//开启一次流式计算
ssc.awaitTermination()
}
}
2.3. 入门案例分析
2.3.1. master的问题
将上述案例中的master由local[*],改成了local,发现只能接收数据,无法处理数据。
上述警告说的是,在本地模式下面如果我们要使用receiver来接收外部数据的时候,必须要将local[N]中N>1。local[N],是给当前作业分配N个线程来进行计算。通过追踪源码,我们发现ssc.socketTextStream中开启了一个线程用来接收外部的数据,也就占用master中配置的线程资源,进而也就无法分配其他的计算资源给计算程序。
注意:如果我们的Streaming程序中,接收数据是基于这个Receiver的方式,必须要配置足够的线程来保证既有资源接收数据,又有资源计算数据,特别的在local模式下面,local[N]中的N>=2。
2.3.2. start()和awaitTermination的问题
要想sparkstremaing程序连续不断的接收并处理外部数据,必须要执行start()和awaitTermination()。
同时,所有的处理的业务逻辑,都必须要在start()方法之前完成,不能在start()方法之后再写其他的业务逻辑。
2.3.3. Receiver
我们在这个案例中,使用socketTextStream接收外部的数据,在这里其实使用了一个Receiver来接收数据的。
3. SparkStreaming和HDFS的整合
object HDFSStreamingOps {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("HDFSStreamingOps")
.setMaster("local")
val ssc = new StreamingContext(conf, Seconds(2))
// val lines:DStream[String] = ssc.textFileStream("file:///E:/data/input")
val lines:DStream[String] = ssc.textFileStream("hdfs://ns1/data/spark")
val ret = lines.flatMap(_.split("\\s+")).map((_, 1)).reduceByKey(_+_)
ret.print()
ssc.start()
ssc.awaitTermination()
}
}
说明:
1、如果加载的是本地目录,必须要通过流的方式写入
2、如果监听的是hdfs中的目录,该目录中的数据可以通过
hdfs dfs -put local-path hdfs-path
hdfs dfs -cp hdfs-src hdfs-dest
但是不可以通过hdfs dfs -mv hdfs-src hdfs-dest
当然我们也可以通过流的方式写入hdfs
3、只能监听目录中的新增文件,不可以监听文件中的新增数据