如何将spark streaming处理结果保存到关系型数据库中

spark streaming是一个分布式高可靠的准实时处理系统,其数据源可以flume、Hdfs、kafka等,其结果可以保存到关系型数据库,HDFS上。保存到HDFS上相对简单,一句话就可以搞定,但是要保存到关系数据库中,相对比较麻烦,既要链接数据库,又要知道数据字段。

我们首先写个wordcount程序测试一下,通过网络发数据到spark streaming

发数据程序如下

[java]  view plain  copy
  1. import java.io.{PrintWriter}  
  2.   
  3. import java.net.ServerSocket  
  4. import scala.io.Source  
  5.   
  6.   
  7. object SaleSimulation {  
  8.     
  9.  def index(length: Int) = {  
  10.   
  11.     import java.util.Random  
  12.    
  13.    val rdm = new Random  
  14.   
  15.     rdm.nextInt(length)  
  16.   
  17.   }  
  18.   
  19.     
  20. def main(args: Array[String]) {  
  21.      
  22.  if (args.length != 3) {  
  23.     
  24.     System.err.println("Usage:   ")  
  25.    
  26.      System.exit(1)  
  27.     
  28.   }  
  29.   
  30.     
  31.   val filename = args(0)  
  32.    
  33.    val lines = Source.fromFile(filename).getLines.toList  
  34.   
  35.     val filerow = lines.length  
  36.   
  37.        val listener = new ServerSocket(args(1).toInt)  
  38.       
  39. while (true) {  
  40.    
  41.      val socket = listener.accept()  
  42.   
  43.       new Thread() {  
  44.      
  45.      override def run = {  
  46.      
  47.        println("Got client connected from: " + socket.getInetAddress)  
  48.      
  49.        val out = new PrintWriter(socket.getOutputStream(), true)  
  50.     
  51.         while (true) {  
  52.       
  53.         Thread.sleep(args(2).toLong)  
  54.     
  55.          val content = lines(index(filerow))  
  56.       
  57.          println(content)  
  58.         
  59.          out.write(content + '\n')  
  60.     
  61.           out.flush()  
  62.       
  63.       }  
  64.      
  65.        socket.close()  
  66.      
  67.      }  
  68.       }.start()  
  69.     
  70.   }  
  71.     
  72. }  
  73.   
  74. }  
打成jar包后运行

[java]  view plain  copy
  1. java -cp spark_streaming_test.jar com.pinganfu.ss.SaleSimulation /spark/people.txt 9999 1000  
spark streaming程序如下

[java]  view plain  copy
  1. import java.sql.{PreparedStatement, Connection, DriverManager}    
  2. import java.util.concurrent.atomic.AtomicInteger    
  3. import org.apache.spark.SparkConf    
  4. import org.apache.spark.streaming.{Seconds, StreamingContext}    
  5. import org.apache.spark.streaming._    
  6. import org.apache.spark.streaming.StreamingContext._    
  7. //No need to call Class.forName("com.mysql.jdbc.Driver") to register Driver?    
  8. object SparkStreamingForPartition {    
  9.   def main(args: Array[String]) {    
  10.     val conf = new SparkConf().setAppName("NetCatWordCount")    
  11.     conf.setMaster("local[3]")    
  12.     val ssc = new StreamingContext(conf, Seconds(5))     
  13.     val dstream = ssc.socketTextStream("hadoopMaster"9999).flatMap(_.split(" ")).map(x => (x, 1)).reduceByKey(_ + _)    
  14.     dstream.foreachRDD(rdd => {    
  15.       //embedded function    
  16.       def func(records: Iterator[(String,Int)]) {    
  17. //Connect the mysql  
  18.         var conn: Connection = null    
  19.         var stmt: PreparedStatement = null    
  20.         try {    
  21.           val url = "jdbc:mysql://hadoopMaster:3306/streaming";    
  22.           val user = "root";    
  23.           val password = "hadoop"    
  24.           conn = DriverManager.getConnection(url, user, password)    
  25.           records.foreach(word => {    
  26.             val sql = "insert into wordcounts values (?,?)";    
  27.             stmt = conn.prepareStatement(sql);    
  28.             stmt.setString(1, word._1)    
  29.             stmt.setInt(2, word._2)    
  30.             stmt.executeUpdate();    
  31.           })    
  32.         } catch {    
  33.           case e: Exception => e.printStackTrace()    
  34.         } finally {    
  35.           if (stmt != null) {    
  36.             stmt.close()    
  37.           }    
  38.           if (conn != null) {    
  39.             conn.close()    
  40.           }    
  41.         }    
  42.       }     
  43.       val repartitionedRDD = rdd.repartition(3)    
  44.       repartitionedRDD.foreachPartition(func)    
  45.     })    
  46.     ssc.start()    
  47.     ssc.awaitTermination()    
  48.   }    
  49. }    
运行结果

如何将spark streaming处理结果保存到关系型数据库中_第1张图片

1. DStream.foreachRDD是一个Output Operation,DStream.foreachRDD是数据落地很常用的方法
2. 获取MySQL Connection的操作应该放在foreachRDD的参数(是一个RDD[T]=>Unit的函数类型),这样,当


foreachRDD方法在每个Worker上执行时,连接是在Worker上创建。如果Connection的获取放到dstream.foreachRDD之

前,那么Connection的获取动作将发生在Driver端,然后通过序列化的方式发送到各个Worker(Connection的序列化通常是无法正确序列化的)
3. Connection的获取在foreachRDD的参数中获取,同时还要在遍历RDD之前获取(调用RDD的foreach方法前获取),如果遍历中获取,那么RDD中的每个record都要打开关闭连接,这对于数据库连接资源将是极大的考验
4. 业务逻辑处理定义在func中,它是在foreachRDD的方法参数体中定义的,如果把func的定义放到外面,即Driver中,貌似也是可以的,Spark会对计算方法通过Broadcast进行广播到各个计算节点。

你可能感兴趣的:(spark)