SparkStreaming之foreachRDD写数据到MySQL、连接池、Window/窗口

文章目录

    • 1、MySQL连接
    • 2、foreachRDD=>foreachPartition=>foreach
    • 3、验证
    • 4、Window/窗口

1、MySQL连接

  def getConnection()={
    Class.forName("com.mysql.jdbc.Driver")
    DriverManager.getConnection("jdbc:mysql://192.168.137.130:3306/rzdb?useSSL=false","root","syncdb123!")
  }

2、foreachRDD=>foreachPartition=>foreach

先开启natcat

[hadoop@vm01 bin]$ nc -lk 8888

MySQL创建一张表

create table wc(word varchar(20),cnt int(10));

然后按照下面4段代码,依次注释和执行。

package com.ruozedata.spark
import java.sql.DriverManager
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}

object SocketWCApp {
  def main(args: Array[String]): Unit = {
    val sparkConf=new SparkConf().setMaster("local[2]").setAppName("SocketWCApp")
    val ssc=new StreamingContext(sparkConf,Seconds(10))

    //From server ==> DStream
    val lines=ssc.socketTextStream("vm01",8888)

    val result=lines.flatMap(_.split(",")).map((_,1)).reduceByKey(_+_)
//    result.print()

//第一段,foreach,一条数据连接一次MySQL,非常消耗资源
//    result.foreachRDD(rdd=>{
//这里是在driver端执行,跨网络需要序列化,这里会有序列化的问题,要放到foreach里面
//      //val connection=getConnection()  
//      rdd.foreach(kv=>{ //foreache是在excutor端执行
//        val connection=getConnection()
//        val sql=s"insert into wc(word,cnt) values ('${kv._1}', '${kv._2}')"
//        connection.createStatement().execute(sql)
//        connection.close()
//      })
//    })

//第二段,优化,foreachPartition,连接MySQL是一个高消耗的事情,一个分区连接一次
//        result.foreachRDD(rdd => {
//          rdd.foreachPartition(partionOfRecords => {
//              val connection = getConnection()
//              partionOfRecords.foreach(kv => {
//                val sql = s"insert into wc(word,cnt) values ('${kv._1}', '${kv._2}')"
//                connection.createStatement().execute(sql)
//              })
//              connection.close()
//          })
//        })

//第三段,优化,foreachPartition,增加连接池,执行后不关闭连接,返回到连接池中
    result.foreachRDD(rdd => {
      rdd.foreachPartition(partionOfRecords => {
        // if(partionOfRecords.size > 0) {
        val connection = ConnectionPool.getConnection().get
        partionOfRecords.foreach(kv => {
          val sql = s"insert into wc(word,cnt) values ('${kv._1}', '${kv._2}')"
          connection.createStatement().execute(sql)
        })
        //connection.close()
        ConnectionPool.returnConnection(connection)
        // }
      })
    })

//第四段,这段了解即可
    //窗口window
//    val windowedWordCounts=result
//      .reduceByKeyAndWindow((a:Int,b:Int)=>(a+b),Seconds(20),Seconds(5))
//    //窗口10秒,每隔5s滑动一次
//    windowedWordCounts.print()


    ssc.start()
    ssc.awaitTermination()
  }

  def getConnection()={
    Class.forName("com.mysql.jdbc.Driver")
    DriverManager.getConnection("jdbc:mysql://192.168.137.130:3306/rzdb?useSSL=false","root","syncdb123!")
  }

}

定义连接池,需要先添加依赖

    <dependency>
      <groupId>com.jolboxgroupId>
      <artifactId>bonecpartifactId>
      <version>0.8.0.RELEASEversion>
    dependency>
package com.ruozedata.spark

import java.sql.{Connection, DriverManager}

import com.jolbox.bonecp.{BoneCP, BoneCPConfig}
import org.slf4j.LoggerFactory


object ConnectionPool {

  val logger=LoggerFactory.getLogger(this.getClass())

  private val pool={
    try{
      Class.forName("com.mysql.jdbc.Driver")
    //  DriverManager.getConnection("jdbc:mysql://192.168.137.130:3306/rzdb?useSSL=false","root","syncdb123!")
      val config = new BoneCPConfig()
      config.setUsername("root")
      config.setPassword("syncdb123!")
      config.setJdbcUrl("jdbc:mysql://192.168.137.130:3306/rzdb?useSSL=false")
      config.setMinConnectionsPerPartition(2) //最小连接数
      config.setMaxConnectionsPerPartition(5) //最大连接数
      config.setCloseConnectionWatch(true)  //关闭的时候要不要监控

      Some(new BoneCP(config))

    }catch {
      case e:Exception=>{
        e.printStackTrace()
        None
      }
    }


  }

  def getConnection():Option[Connection]={
    pool match {
        case  Some(pool)=> Some(pool.getConnection)
        case None=>None

    }

  }

  def  returnConnection(connection:Connection)={
    if(null != connection){
      connection.close() //这个地方不能关闭,应该要返回到池里面去吃才行
    }

  }

}

3、验证

[hadoop@vm01 bin]$ nc -lk 8888
hello,hadoop
hello,spark
mysql> select * from wc;
+--------+------+
| word   | cnt  |
+--------+------+
| hello  |    1 |
| hadoop |    1 |
| hello  |    1 |
| spark  |    1 |
+--------+------+
12 rows in set (0.00 sec)

为什么没有看到hello累加呢,因为SparkStreaming是10秒一个批次,输入第一行就执行了第一个批次,然后输入第二行,要等到第二个批次。

4、Window/窗口

window length : 一个长度为3 , 比如下图123 345
window interval : 滑动间隔 ,上面就是滑动2个间隔开一个窗口
那么第一个窗口统计的是123的数据,第二个窗口统计的是345
SparkStreaming之foreachRDD写数据到MySQL、连接池、Window/窗口_第1张图片

你可能感兴趣的:(Spark2)