文章目录
1、SparkStreaming案例
1.1 代码如下:
- 导入pom依赖,如下
- 先创建SparkConf
- 设置StreamingContext批处理时间间隔
- 使用socketTextStream加载数据源:主机号、端口、存储方式
- 对数据进行处理
- 数据写出,print()
- 启动程序:启动线程监听的意思
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-streaming -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka -->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.12</artifactId>
<version>${kafka.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients -->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>${kafka.version}</version>
</dependency>
<!-- 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>${spark.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-sql -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
import org.apache.spark.SparkConf
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.{Seconds, StreamingContext}
object WordCount {
def main(args: Array[String]): Unit = {
//模板代码:1.创建SparkConf
val conf = new SparkConf().setAppName(this.getClass.getName).setMaster("local[4]")
val ssc = new StreamingContext(conf, Seconds(5))
//加载数据源,三个参数:主机、端口、存储方式
val lines = ssc.socketTextStream("chust01", 9999, StorageLevel.MEMORY_AND_DISK_SER_2)
//对数据进行处理[WordCount]
val result = lines.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)
//数据写出
result.print()
//启动程序
ssc.start()
ssc.awaitTermination()
}
}
1.2 shell作为数据输入端
使用nc插件连接IDEA并输入内容
#安装nc插件
yum -y install nc
#启动nc端口,并输入内容
nc -lk 9999
1.3 执行测试
运行IDEA的Streaming程序,并在nc控制台输入内容,即可在IDEA控制台查看到输出
1.4 踩坑点
- Kafka和SparkStreaming程序的pom依赖有冲突,运行Streaming时要注释掉kafka的pom依赖
- Kafka依赖自带的json是2.9.8版本的,而spark仅支持2,6版本的,需要更改json版本依赖
2、使用Spark Streaming处理HDFS上的文件
2.1 代码如下
//演示示例2:使用Spark Streaming处理HDFS上的文件
object HDFSWordCount {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[*]").setAppName(this.getClass.getSimpleName)
val ssc = new StreamingContext(conf, Seconds(5))
val lines = ssc.textFileStream("hdfs://192.168.221.140:9000/kb10/0120test/")
val wordCount = lines.flatMap(_.split(" ")).map(x => (x, 1)).reduceByKey(_ + _)
wordCount.print()
ssc.start()
ssc.awaitTermination()
}
}
2.2 上传文件
运行IDEA代码,往hdfs对应目录上传文件,测试的话可以使用hdfs dfs -put命令
2.3 踩坑点
- 集群上传文件的时钟和网络不同步,重启ntp即可。该问题不会报错,比较难缠。
3、使用Spark Streaming处理有状态的数据
3.1 流程分析
- 该流程目的是保留之前的数据累加当前批处理的数据,因此需要设置一个检查点保留数据的offset
- 这里使用网络接口文本流传输数据
- 处理完数据后,使用updateStateByKey方法,传入一个自定义的偏函数(这里测试用例为wordcount,因此方法为累计)
- updateStateByKey方法,传入的参数为当前序列Seq[Int]和之前的值Option[Int]类型,意思是当前序列的计算(多数情况下为累加)和前一批次计算的值(第一次的话,之前是没有值的,因此用的Option偏函数)
3.2 代码如下
object StatefulWordCount {
def main(args: Array[String]): Unit = {
// 模板代码: ①创建SparkConf, ②创建StreamingContext, 第二个参数是批处理间隔大小
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getName)
.setMaster("local[4]")
val ssc = new StreamingContext(conf, Seconds(5))
// 设置checkpoint目录, 用来保存状态
ssc.checkpoint("D:\\JavaProjects\\ClassStudy\\Scala\\" +
"sparkdemo\\files\\09checkpoint")
// 1.加载socket数据源, 三个参数:①主机名或ip地址, ②端口号, ③存储级别
val lines= ssc.socketTextStream("chust01"
, 9999, StorageLevel.MEMORY_AND_DISK_SER_2)
// 2.对数据进行处理[WordCount]
// 使用了 updateStateByKey 状态类的算子, 可以统计截止到当前位置的累加值, 需要传入一个更新状态的函数
val result: DStream[(String, Int)] = lines.flatMap(_.split("\\s+"))
.map((_, 1))
//.updateStateByKey((x,y)=>Some(x.sum+y.getOrElse(0)))
.updateStateByKey(updateFunction)
// 3.数据写出[print]
result.print()
// 4.启动程序
ssc.start()
ssc.awaitTermination()
}
// 定义一个更新状态的函数
// 接收两个参数: ①当前批次的value值的序列 ②前一批次的统计状态值
// 返回值: 更新状态值
def updateFunction(currentValues: Seq[Int], preValues: Option[Int]) = {
val curr: Int = currentValues.sum
val pre: Int = preValues.getOrElse(0)
Some(curr + pre)
}
}
3.3 测试
其他和上述流程没啥区别,代码写好了之后,在虚拟机上启动nc端口,并输入内容nc -lk 9999,然后运行IDEA程序,输入数据即可看到效果。
3.4 踩坑点
偏函数Some方法不用导包,如果导了系统推荐的包则会报错
4、使用Spark Streaming整合Spark SQL
4.1 流程分析
- 定义一个业务所需要的样例类
- 创建SparkSession
- 使用foreachRDD逐条处理
- 把每一条视作一个spark,创建DataFrame
- 输出即可,其他正常
- 注意点:可以创建两次SparkSession;也可以在foreachRDD内部只创建一次
4.2 代码如下
//定义单词的case class类
case class Word(word: String)
object SparkStreamingDemo {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setAppName("NetworkSQLWordCount").setMaster("local[2]")
val ssc = new StreamingContext(sparkConf, Seconds(5))
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
val lines = ssc.socketTextStream("single", 9999)
val result = lines.flatMap(_.split(" "))
result.foreachRDD(rdd => {
import spark.implicits._
val structType = StructType(
Array(
StructField("word", StringType)
)
)
val rddrow = rdd.map(x => Row(x))
val df = spark.createDataFrame(rddrow, structType)
import org.apache.spark.sql.functions._
// df.createOrReplaceTempView("word")
// spark.sql(
// """
// |select word,count(1)
// |from word
// |group by word
// |""".stripMargin).show()
df.groupBy($"word").agg(count($"word"))
})
ssc.start()
ssc.awaitTermination()
}
}
5、使用Spark Streaming处理Flume输出的数据-push
当采用 Push 方式时,Flume 架构为:netcat->memory->avro
6、使用Spark Streaming处理Flume输出的数据-pull
当采用 Pull 方式时,Flume 架构为:netcat->memory->SparkSink
7、使用Spark Streaming整合Kafka的Direct风格
7.1 流程分析
- 准备连接kafka时的一些参数, 包括一个topic的迭代器以及Kafka参数的一个Map
- 使用Kafka直连的方式,选择合适的分区策略和订阅方式
- 从kafka中获取到的数据取出value值
- 对数据进行处理和写出。最后启动程序
7.2 代码如下
object SparkStreaming {
def main(args: Array[String]): Unit = {
// 模板代码: ①创建SparkConf, ②创建StreamingContext, 第二个参数是批处理间隔大小
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getName).setMaster("local[4]")
val ssc = new StreamingContext(conf, Seconds(5))
// 1.准备连接kafka时的一些参数, 包括一个topic的迭代器以及Kafka参数的一个Map
val topices = "test"
val topicsSet: Set[String] = topices.split(",").toSet
val kafkaParams = Map[String, String](
"bootstrap.servers" -> "chust01:9092",
"group.id" -> "testGroup1",
"enable.auto.commit" -> "true",
"key.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer",
"value.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer"
)
// 使用Kafka直连的方式
val messages: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream(ssc,
LocationStrategies.PreferConsistent, // 分配策略1:每个Executor作为一个consumer,与kafka中的Partition对应
//LocationStrategies.PreferBrokers//Executor与Broker在相同节点
//LocationStrategies.PreferBrokers//Executor和Partition进行分区映射,手动指定
ConsumerStrategies.Subscribe(topicsSet, kafkaParams) // 订阅方式, 按照topic订阅
//ConsumerStrategies//指定固定的分区集合,按照分区订阅
)
// 将从kafka中获取到的数据取出value值
val lines: DStream[String] = messages.map(_.value())
// 2.对数据进行处理[WordCount]
val result: DStream[(String, Int)] = lines.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)
// 3.数据写出[print]
result.print()
// 4.启动程序
ssc.start()
ssc.awaitTermination()
}
}