Spark-Streaming基础使用

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()
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值