SparkStreaming第一课

前期准备:

  1. 搭建好hadoop和spark集群
  2. 掌握SparkCore知识
  3. 掌握SparkSQL知识
  4. 准备好IDEA开发环境

内容概要

本次课是SparkStreaming的第一次课,主要讲解SparkStreaming的运行原理,常用算子的使用。

课程目标

  • 掌握SparkStreaming运行原理
  • 掌握SparkStreaming基本算子使用

知识要点

1、SparkStreaming简介

  • SparkStreaming是对于Spark核心API的拓展,从而支持对于实时数据流的可拓展,高吞吐量和容错性流处理。
  • 数据可以由多个源取得,例如:Kafka,Flume,Twitter,ZeroMQ,Kinesis或者TCP接口,同时可以使用由如map,reduce,join和window这样的高层接口描述的复杂算法进行处理。最终,处理过的数据可以被推送到文件系统,数据库和HDFS。
  • Spark Streaming 是基于spark的流式批处理引擎,其基本原理是把输入数据以某一时间间隔批量的处理,当批处理间隔缩短到秒级时,便可以用于处理实时数据流。
  • 在 Spark Streaming 中,处理数据的单位是一批而不是单条,而数据采集却是逐条进行的,因此 Spark Streaming 系统需要设置间隔使得数据汇总到一定的量后再一并操作,这个间隔就是批处理间隔。批处理间隔是 Spark Streaming 的核心概念和关键参数,它决定了 Spark Streaming 提交作业的频率和数据处理的延迟,同时也影响着数据处理的吞吐量和性能。
    streaming架构

2、SparkStreaming架构流程

sparkStreaming架构流程

3、 SparkStreaming程序入口

val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount")
val ssc = new StreamingContext(conf, Seconds(1))
//或者
val ssc = new StreamingContext(new SparkContext(conf), Seconds(1))

4、什么是DStream

  • 离散数据流或者DStream是SparkStreaming提供的基本抽象。其表现数据的连续流,这个输入数据流可以来自于源,也可以来自于转换输入流产生的已处理数据流。内部而言,一个DStream以一系列连续的RDDs所展现,这些RDD是Spark对于不变的,分布式数据集的抽象。一个DStream中的每个RDD都包含来自一定间隔的数据,如下图

在这里插入图片描述

  • 在DStream上使用的任何操作都会转换为针对底层RDD的操作。例如:之前那个将行的流转变为词流的例子中,flatMap操作应用于行DStream的每个RDD上 从而产生words DStream的RDD。如下图:
    在这里插入图片描述
    DStream逻辑图

5、DStream算子操作

5.1 Transformations

  • 实现把一个DStream转换生成一个新的DStream,延迟加载不会触发任务的执行
TransformationMeaning
map(func)对DStream中的各个元素进行func函数操作,然后返回一个新的DStream
flatMap(func)与map方法类似,只不过各个输入项可以被输出为零个或多个输出项
filter(func)过滤出所有函数func返回值为true的DStream元素并返回一个新的DStream
repartition(numPartitions)增加或减少DStream中的分区数,从而改变DStream的并行度union(otherStream)
count()通过对DStream中的各个RDD中的元素进行计数,然后返回只有一个元素的RDD构成的DStream
reduce(func)对源DStream中的各个RDD中的元素利用func进行聚合操作,然后返回只有一个元素的RDD构成的新的DStream.
countByValue()对于元素类型为K的DStream,返回一个元素为(K,Long)键值对形式的新的DStream,Long对应的值为源DStream中各个RDD的key出现的次数
reduceByKey(func, [numTasks])利用func函数对源DStream中的key进行聚合操作,然后返回新的(K,V)对构成的DStream
join(otherStream, [numTasks])输入为(K,V)、(K,W)类型的DStream,返回一个新的(K,(V,W))类型的DStream
cogroup(otherStream, [numTasks])输入为(K,V)、(K,W)类型的DStream,返回一个新的 (K, Seq[V], Seq[W]) 元组类型的DStream
transform(func)通过RDD-to-RDD函数作用于DStream中的各个RDD,可以是任意的RDD操作,从而返回一个新的RDD
updateStateByKey(func)根据key的之前状态值和key的新值,对key进行更新,返回一个新状态的DStream
reduceByKeyAndWindow窗口函数操作,实现按照window窗口大小来进行计算

5.2 Output Operations

  • 输出算子操作,触发任务的真正运行
Output OperationMeaning
print()打印到控制台
saveAsTextFiles(prefix, [suffix])保存流的内容为文本文件,文件名为"prefix-TIME_IN_MS[.suffix]".
saveAsObjectFiles(prefix, [suffix])保存流的内容为SequenceFile,文件名为 “prefix-TIME_IN_MS[.suffix]”.
saveAsHadoopFiles(prefix, [suffix])保存流的内容为hadoop文件,文件名为 “prefix-TIME_IN_MS[.suffix]”.
foreachRDD(func)对Dstream里面的每个RDD执行func

6、数据源

6.1 socket数据源

  • 需求:

    sparkStreaming实时接收socket数据,实现单词计数

  • 业务处理流程图
    在这里插入图片描述

  • 安装socket服务
    首先在linux服务器node01上用yum 安装nc工具,nc命令是netcat命令的简称,它是用来设置路由器。我们可以利用它向某个端口发送数据。

sudo  yum -y install nc

执行命令向指定的端口发送数据

nc -lk 9999 
  • 代码开发
    pom.xml配置
<properties>
        <scala.version>2.11.8</scala.version>
        <spark.version>2.3.3</spark.version>
</properties>

 <dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming_2.11</artifactId>
            <version>${spark.version}</version>
        </dependency>

</dependencies>

 <build>
        <sourceDirectory>src/main/scala</sourceDirectory>
        <testSourceDirectory>src/test/scala</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.2.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                        <configuration>
                            <args>
                                <arg>-dependencyfile</arg>
                                <arg>${project.build.directory}/.scala_dependencies</arg>
                            </args>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <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>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass></mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
  • 开发sparkStreaming程序
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Seconds, StreamingContext}

/**
* sparkStreaming接受socket数据实现单词计数程序
 */
object SocketWordCount {

 def main(args: Array[String]): Unit = {
   Logger.getLogger("org").setLevel(Level.ERROR)

   // todo: 1、创建SparkConf对象
     val sparkConf: SparkConf = new SparkConf().setAppName("TcpWordCount").setMaster("local[2]")

  // todo: 2、创建StreamingContext对象
     val ssc = new StreamingContext(sparkConf,Seconds(2))

  //todo: 3、接受socket数据
     val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node01",9999)

  //todo: 4、对数据进行处理
     val result: DStream[(String, Int)] = socketTextStream.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)

  //todo: 5、打印结果
      result.print()

  //todo: 6、开启流式计算
     ssc.start()
     ssc.awaitTermination()
     
 }
}

6.2 HDFS数据源

  • 需求:
    通过sparkStreaming监控hdfs上的目录,有新的文件产生,就把数据拉取过来进行处理

  • 业务处理流程图
    在这里插入图片描述

import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}

/**
  * HDFS数据源
  */
object HdfsWordCount {
    
  def main(args: Array[String]): Unit = {
    Logger.getLogger("org").setLevel(Level.ERROR)

    // todo: 1、创建SparkConf对象
    val sparkConf: SparkConf = new SparkConf().setAppName("HdfsWordCount").setMaster("local[2]")

    // todo: 2、创建StreamingContext对象
    val ssc = new StreamingContext(sparkConf,Seconds(2))

    //todo: 3、监控hdfs目录数据
    val textFileStream: DStream[String] = ssc.textFileStream("hdfs://node01:8020/data")


    //todo: 4、对数据进行处理
    val result: DStream[(String, Int)] = textFileStream.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)

    //todo: 5、打印结果
    result.print()


    //todo: 6、开启流式计算
    ssc.start()
    ssc.awaitTermination()
    
  }
}

6.3 自定义数据源

/**
  * 自定义一个Receiver,这个Receiver从socket中接收数据
  * 使用方式:nc -lk 8888
  */
  import java.io.{BufferedReader, InputStreamReader}
import java.net.Socket
import java.nio.charset.StandardCharsets

import org.apache.spark.SparkConf
import org.apache.spark.internal.Logging
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.receiver.Receiver

/**
  * 自定义数据源
  */
object CustomReceiver {

  def main(args: Array[String]): Unit = {
   Logger.getLogger("org").setLevel(Level.ERROR)

    // todo: 1、创建SparkConf对象
    val sparkConf: SparkConf = new SparkConf()
                                              .setAppName("CustomReceiver")
                                              .setMaster("local[2]")

    // todo: 2、创建StreamingContext对象
    val ssc = new StreamingContext(sparkConf,Seconds(2))

    //todo: 3、调用 receiverStream api,将自定义的Receiver传进去
   val receiverStream = ssc.receiverStream(new CustomReceiver("node01",8888))

    //todo: 4、对数据进行处理
    val result: DStream[(String, Int)] = receiverStream
                                                      .flatMap(_.split(" "))
                                                      .map((_,1))
                                                      .reduceByKey(_+_)
    //todo: 5、打印结果
    result.print()

    //todo: 6、开启流式计算
    ssc.start()
    ssc.awaitTermination()

  }

}

/**
  * 自定义source数据源
  * @param host
  * @param port
  */
class CustomReceiver(host:String,port:Int) extends Receiver[String](StorageLevel.MEMORY_AND_DISK_SER) with Logging{
  override def onStart(): Unit ={
    //启动一个线程,开始接受数据
      new Thread("socket receiver"){
        override def run(): Unit = {
            receive()
          }
      }.start()
  }

  /** Create a socket connection and receive data until receiver is stopped */
  private def receive() {
    var socket: Socket = null
    var userInput: String = null
    try {
      logInfo("Connecting to " + host + ":" + port)
      socket = new Socket(host, port)
      logInfo("Connected to " + host + ":" + port)
      val reader = new BufferedReader(
        new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))

      userInput = reader.readLine()
      while(!isStopped && userInput != null) {
        store(userInput)
        userInput = reader.readLine()
      }
      reader.close()
      socket.close()
      logInfo("Stopped receiving")
      restart("Trying to connect again")
    } catch {
      case e: java.net.ConnectException =>
        restart("Error connecting to " + host + ":" + port, e)
      case t: Throwable =>
        restart("Error receiving data", t)
    }
}

  override def onStop(): Unit ={

  }
}

6.4 Kafka数据源

  • 非常重要的数据源,会放在后面重点详细的讲解

7、SparkStreaming 任务提交集群运行

  • 开发wordcount程序,然后打包上传到集群,并打开任务运行界面,查看一下任务运行情况。
bin/spark-submit \
--master spark://node01:7077 \
--class com.aaa.streaming.Demo \
--executor-memory 1g \
--total-executor-cores 2 \
original-sparkStreamingStudy-1.0-SNAPSHOT.jar 

8、Transformation高级算子

8.1 updateStateByKey

  • 需求
    sparkStreaming接受socket数据实现所有批次的单词次数累加

  • 代码开发

import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Seconds, StreamingContext}

/**
* todo: 实现把所有批次的单词出现的次数累加
 */
object UpdateStateBykeyWordCount {

 def main(args: Array[String]): Unit = {
   Logger.getLogger("org").setLevel(Level.ERROR)

   // todo: 1、创建SparkConf对象
   val sparkConf: SparkConf = new SparkConf().setAppName("TcpWordCount").setMaster("local[2]")

   // todo: 2、创建StreamingContext对象
   val ssc = new StreamingContext(sparkConf,Seconds(2))

     //需要设置checkpoint目录,用于保存之前批次的结果数据,该目录一般指向hdfs路径
      ssc.checkpoint("hdfs://node01:8020/ck")

   //todo: 3、接受socket数据
   val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node01",9999)

   //todo: 4、对数据进行处理
   val wordAndOneDstream: DStream[(String, Int)] = socketTextStream.flatMap(_.split(" ")).map((_,1))

   val result: DStream[(String, Int)] = wordAndOneDstream.updateStateByKey(updateFunc)

   //todo: 5、打印结果
   result.print()

   //todo: 6、开启流式计算
   ssc.start()
   ssc.awaitTermination()

 }

 //currentValue:当前批次中每一个单词出现的所有的1
 //historyValues:之前批次中每个单词出现的总次数,Option类型表示存在或者不存在。 Some表示存在有值,None表示没有
 def updateFunc(currentValue:Seq[Int], historyValues:Option[Int]):Option[Int] = {
   val newValue: Int = currentValue.sum + historyValues.getOrElse(0)
   Some(newValue)
 }
}

8.2 mapWithState

  • 需求
    sparkStreaming接受socket数据实现所有批次的单词次数累加

  • 代码开发

import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.{DStream, MapWithStateDStream, ReceiverInputDStream}
import org.apache.spark.streaming._

/**
  * todo: mapWithState实现把所有批次的单词出现的次数累加
  * --性能更好
  */
object MapWithStateWordCount {

  def main(args: Array[String]): Unit = {
    Logger.getLogger("org").setLevel(Level.ERROR)

    // todo: 1、创建SparkConf对象
    val sparkConf: SparkConf = new SparkConf().setAppName("MapWithStateWordCount").setMaster("local[2]")

    // todo: 2、创建StreamingContext对象
    val ssc = new StreamingContext(sparkConf,Seconds(2))

      val initRDD: RDD[(String, Int)] = ssc.sparkContext.parallelize((List(("hadoop",10),("spark",20))))

    //需要设置checkpoint目录,用于保存之前批次的结果数据,该目录一般指向hdfs路径
       ssc.checkpoint("hdfs://node01:8020/ck")

    //todo: 3、接受socket数据
    val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node01",9999)

    //todo: 4、对数据进行处理
    val wordAndOneDstream: DStream[(String, Int)] = socketTextStream.flatMap(_.split(" ")).map((_,1))


     val stateSpec=StateSpec.function((time:Time,key:String,currentValue:Option[Int],historyState:State[Int])=>{

         //当前批次结果与历史批次的结果累加
        val sumValue: Int = currentValue.getOrElse(0)+ historyState.getOption().getOrElse(0)
        val output=(key,sumValue)


        if(!historyState.isTimingOut()){
          historyState.update(sumValue)
        }

       Some(output)
       //给一个初始的结果initRDD
       //timeout: 当一个key超过这个时间没有接收到数据的时候,这个key以及对应的状态会被移除掉
     }).initialState(initRDD).timeout(Durations.seconds(5))


     //todo: 使用mapWithState方法,实现累加
     val result: MapWithStateDStream[String, Int, Int, (String, Int)] = wordAndOneDstream.mapWithState(stateSpec)

    //todo: 5、打印所有批次的结果数据
    result.stateSnapshots().print()

    //todo: 6、开启流式计算
    ssc.start()
    ssc.awaitTermination()
  }
}

  • 小结
    • 特点:
      (1)若要清除某个key的状态,可在自定义的方法中调用state.remove()
      (2)若要设置状态超时时间,可以调用StateSpec.function(mappingFunc).timeout()方法设置
      (3)若要添加初始化的状态,可以调用StateSpec.function(mappingFunc).initialState(initialRDD)方法
      (4)性能比updateStateByKey好

8.3 transform

  • 需求
    获取每一个批次中单词出现次数最多的前3位
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}

/**
 * todo: 获取每一个批次中单词出现次数最多的前3位
  */
object TransformWordCount {

  def main(args: Array[String]): Unit = {

    Logger.getLogger("org").setLevel(Level.ERROR)

    // todo: 1、创建SparkConf对象
    val sparkConf: SparkConf = new SparkConf().setAppName("TransformWordCount").setMaster("local[2]")

    // todo: 2、创建StreamingContext对象
    val ssc = new StreamingContext(sparkConf,Seconds(2))

    //todo: 3、接受socket数据
    val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node01",9999)

    //todo: 4、对数据进行处理
    val result: DStream[(String, Int)] = socketTextStream.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)


    //todo: 5、将Dstream进行transform方法操作
    val sortedDstream: DStream[(String, Int)] = result.transform(rdd => {
      //对单词出现的次数进行排序
      val sortedRDD: RDD[(String, Int)] = rdd.sortBy(_._2, false)

      val top3: Array[(String, Int)] = sortedRDD.take(3)
      println("------------top3----------start")
      top3.foreach(println)
      println("------------top3------------end")
      sortedRDD
    })

    //todo: 6、打印该批次中所有单词按照次数降序的结果
    sortedDstream.print()

    //todo: 7、开启流式计算
    ssc.start()
    ssc.awaitTermination()

  }
}

8.4 window

  • Window Operations可以设置窗口的大小和滑动窗口的间隔来动态的获取当前Steaming的允许状态。
  • 所有基于窗口的操作都需要两个参数,分别为窗口时长以及滑动步长。
    • 窗口时长:计算内容的时间范围
    • 滑动步长:每隔多久触发一次计算
  • 注意:这两者都必须为采集周期大小的整数倍。
  • 需求:
    2秒一个批次,实现每隔4秒统计6秒窗口的数据结果
  • 代码开发
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}

/**
 * todo: 2秒一个批次,实现每隔4秒统计6秒窗口的数据结果
 */
object ReduceByKeyAndWindowWordCount {

 def main(args: Array[String]): Unit = {
   Logger.getLogger("org").setLevel(Level.ERROR)

   // todo: 1、创建SparkConf对象
   val sparkConf: SparkConf = new SparkConf().setAppName("ReduceByKeyAndWindowWordCount").setMaster("local[2]")

   // todo: 2、创建StreamingContext对象
   val ssc = new StreamingContext(sparkConf,Seconds(2))

   //todo: 3、接受socket数据
   val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node01",9999)

   //todo: 4、对数据进行处理
   val result: DStream[(String, Int)] = socketTextStream.flatMap(_.split(" ")).map((_,1))


   //todo: 5、每隔4秒统计6秒的数据
   /**
     * 该方法需要三个参数:
     * reduceFunc: (V, V) => V,  ----------> 就是一个函数
     * windowDuration: Duration, ----------> 窗口的大小(时间单位),该窗口会包含N个批次的数据
     * slideDuration: Duration   ----------> 滑动窗口的时间间隔,表示每隔多久计算一次
     */
   val windowDStream: DStream[(String, Int)] = result.reduceByKeyAndWindow((x:Int,y:Int)=>x+y,Seconds(6),Seconds(4))

   //todo: 6、打印该批次中所有单词按照次数降序的结果
   windowDStream.print()

   //todo: 7、开启流式计算
   ssc.start()
   ssc.awaitTermination()


 }

}

9、Output 算子

  • 核心算子foreachRDD实战

  • 需求:
    sparkStreaming把处理的结果数据写入到mysql表中进行存储

  • 代码开发

import java.sql.DriverManager

import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}

/**
 *  todo: 将WordCount案例中得到的结果通过foreachRDD保存结果到mysql中
 */
object WordCountForeachRDD {
 def main(args: Array[String]): Unit = {
   Logger.getLogger("org").setLevel(Level.ERROR)

   // todo: 1、创建SparkConf对象
   val sparkConf: SparkConf = new SparkConf().setAppName("WordCountForeachRDD").setMaster("local[2]")

   // todo: 2、创建StreamingContext对象
   val ssc = new StreamingContext(sparkConf,Seconds(2))

   //todo: 3、接受socket数据
   val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node01",9999)

   //todo: 4、对数据进行处理
   val result: DStream[(String, Int)] = socketTextStream.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)


   //todo: 5、保存结果到mysql表中

     //todo:方案一(有问题)
     result.foreachRDD(rdd =>{
        //注意这里创建的对象都是在Driver端
       val conn = DriverManager.getConnection("jdbc:mysql://node03:3306/test", "root", "123456")
       val statement = conn.prepareStatement(s"insert into wordcount(word, count) values (?, ?)")

       rdd.foreach { record =>
         //这一块代码的执行是在executor端,需要进行网络传输,会出现task not serializable 异常
         statement.setString(1, record._1)
         statement.setInt(2, record._2)
         statement.execute()
       }
       statement.close()
       conn.close()
     })

    //todo: 方案二

   result.foreachRDD(rdd=>{
       //遍历
     rdd.foreach { record =>
       val conn = DriverManager.getConnection("jdbc:mysql://node03:3306/test", "root", "123456")
       val statement = conn.prepareStatement(s"insert into wordcount(word, count) values (?, ?)")
       statement.setString(1, record._1)
       statement.setInt(2, record._2)
       statement.execute()

       statement.close()
       conn.close()
     }
   })

   //todo: 方案三
   result.foreachRDD(rdd=>{
       rdd.foreachPartition( iter =>{
         val conn = DriverManager.getConnection("jdbc:mysql://node03:3306/test", "root", "123456")
         val statement = conn.prepareStatement(s"insert into wordcount(word, count) values (?, ?)")

         iter.foreach( record =>{
           statement.setString(1, record._1)
           statement.setInt(2, record._2)
           statement.execute()

         })
         statement.close()
         conn.close()
       })
   })

     
   //todo: 方案四
   result.foreachRDD(rdd=>{
     rdd.foreachPartition( iter =>{
       val conn = DriverManager.getConnection("jdbc:mysql://node03:3306/test", "root", "123456")
       val statement = conn.prepareStatement(s"insert into wordcount(word, count) values (?, ?)")
       //关闭自动提交
       conn.setAutoCommit(false);
       iter.foreach( record =>{
         statement.setString(1, record._1)
         statement.setInt(2, record._2)
         //添加到一个批次
         statement.addBatch()

       })
       //批量提交该分区所有数据
       statement.executeBatch()
       conn.commit()
       // 关闭资源
       statement.close()
       conn.close()
     })
   })



   //todo: 6、开启流式计算
   ssc.start()
   ssc.awaitTermination()

 }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值