零基础开发spark实时计算程序(2)

穿精又带淫゛_ 提交于 2020-03-11 13:09:17

主要问题

处理流式数据的两种方法

静态数据的几种格式

要介绍流式数据的处理,首先要介绍spark的几种静态的数据结构:RDD、dataset和dataframe。
简单来讲,RDD是spark最基础的数据,可以看出一行行独立的数据,每一行内部是封闭的黑箱,在MapReduce之前不知道是啥(MapReduce是hadoop的一种计算模型,浅显一点理解就是做筛选统计之类的活的,就是下图紫色的过程)。

图出自https://blog.csdn.net/MrZhangBaby/article/details/88840635,看不清可以点进去看

在这里插入图片描述
Dataset是整理过的RDD,同样可以理解为一行行的数据,只是里面更有序(有了固定的结构schema)
Dataframe就是Python和R里的那种dataframe,也就是最常见的带表头的表,是Dataset的特例,链接里的图就很形象

https://blog.csdn.net/weixin_42702831/article/details/82492421

RDD
在这里插入图片描述
Dataframe
在这里插入图片描述
Dataset
在这里插入图片描述或者是在这里插入图片描述

流式数据的2种处理方式

从文件里读数据,读一次就产生一个静态数据,而像日志之类的记录这种不断增长的数据,可以看做是一个瀑布流,源源不断的增长。针对这种流式数据(data stream)的处理,要么用flink(flink其实是处理流式数据更专业的方法,和spark、kafka一样都是apache的顶级项目,阿里开源的blink就是包含于flink的),要么用spark,这里由于自身原因使用spark。用spark一般有2种方法

  • RDD转Dstream
    因为RDD是最基础的数据格式,所以数据流最开始就是不断增长的RDD。不断增长怎么统计计算呢?切片!举个栗子,按照5分钟的间隔,将间隔内的数据增量作为整体(一个batch)进行计算。切片的原理很简单,可是5分钟的间隔感觉不够实时,那按微积分的思想,切成0.1s的间隔,就可以看成实时数据了。。。事实上batch的划分还是受限于数据量和可用资源,分的越细,耗费的资源自然就越大。官方文档里给的例子是1秒,想来应该是够用的。
    在这里插入图片描述
  • RDD转Dataframe
    Dstream是Discretized Streams的缩写,顾名思义,是离散的。处理过程是先将整体打散,分别处理,然后再合起来。而RDD转Dataframe使用StructureStreaming,将不断增长的数据,变换为一张不断增长的具有结构(structure或者说schema)的大表,进而可以把它当成写SQL一样,直接处理当前时间的整体。如下图所示。
    在这里插入图片描述

计算结果输出到kafka

使用RDD的Dstream(旧接口)

其实官方的例子已经很完善了,这里尽可能简化一下项目的代码,贴一些注释,可以结合着文档的例子看看。

object SparkStreamingObj {
  def main(args: Array[String]): Unit = {		//上面的都是固定写法,和java一样
    val conf = new SparkConf()		//spark的入口设置
      .setMaster("local[*]")
      .setAppName("test")		//设置
    val processInterval = '1'		//这个就是切片的间隔,可以直接设置成1s
    val ssc = new StreamingContext(conf, Seconds(processInterval))

    val brokers = 'localhost:9092'		//这里是数据的来源,kafka的地址
    val topics = 'topic1,topic2'		//kafka的topic

    //kafka配置参数
    val kafkaParams: Map[String, String] = Map[String, String](
      "metadata.broker.list" -> brokers,
      "serializer.class" -> "kafka.serializer.StringEncoder")
    val topicslist = topics.split(",").toSet
    val brokerList = host.split(",").toList

    val stream = createDirectStream(ssc, kafkaParams, topicslist )//从kafka接收数据

    val outtopic = "outtopic"		//以kafka的方式将结果输出
    val outbrokers = "localhost:9092"	//	kafka输出地址
    val kafkaProducer: Broadcast[KafkaSink[String, String]] = {
      val kafkaProducerConfig = {
        val p = new Properties()
        p.setProperty("bootstrap.servers", outbrokers)
        p.setProperty("key.serializer", classOf[StringSerializer].getName)
        p.setProperty("value.serializer", classOf[StringSerializer].getName)
        p
      }
      ssc.sparkContext.broadcast(KafkaSink[String, String](kafkaProducerConfig))}
    val windows = stream.map(_._2).window(Seconds(300),Seconds(60))	//设置时间窗,作为计算间隔

    windows.foreachRDD{ rdd =>
      val spark = SparkSession
        .builder
        .config(rdd.sparkContext.getConf)
//        .config("spark.sql.crossJoin.enabled","true")//开启两表连接功能
        .getOrCreate()
      import spark.implicits._ 
      
      val df = rdd.toDF("json")		

      val DF=df.select(
        get_json_object($"json","$.meta.table").alias("table"),
        .filter(" table ='table1'")

      DF.createOrReplaceTempView("table_temp")		//创建临时表,便于sql直接查询

      val sqlText = "select count(*) as index from table_temp "

      val result = spark.sql(sqlText)
      .withColumn("sys",lit("sys1"))	//withColum用于拼接列
      .withColumn("timestamp", functions.current_timestamp())	//current_timestamp插入当前时间
//      result.show(truncate = false)
      //      println("=====================================================")
      //输出到kafka
      val kafaValue = result.toJSON.collectAsList().toString
      kafkaProducer.value.send(outtopic,kafaValue.substring(1, (kafaValue.length()-1)))// 将list的[]去掉,变成json
    }
    ssc.start()
    ssc.awaitTermination()
  }
  def createDirectStream(scc: StreamingContext, kafkaParam: Map[String, String], topics: Set[String]) = {
    KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](scc, kafkaParam, topics)
  }
}

使用Structure Streaming(新接口)

object SparkStreamingObj {
  def main(args: Array[String]): Unit = {//导入kafka参数
    val app_conf = ConfigFactory.load("application.conf")
    val brokers = app_conf.getString("kafka-config.brokers")
    val topics = app_conf.getString("kafka-config.topic")

    val spark = SparkSession
      .builder()
      .appName("appTest")
//      .master("local[*]") //跑集群要注释掉
      .getOrCreate()
      
    import spark.implicits._
    
    val inputstream = spark.readStream
      .format("kafka")
      .option("kafka.bootstrap.servers", brokers)
      .option("subscribe", "topic_test") //官方用法
//      .option("startingoffset", "smallest")
//      .option("topics", topics) //第三方08kafka用法
      .load()

    //低版本spark无法自动识别字符串的schema,需要手动指定。
    //kafka10版本以上也可以通过给一个实例自动推断schema
    val schema = StructType(Seq(
      StructField("meta",StringType,true), StructField("data",StructType(Seq(
        StructField("ID",StringType,true),
        StructField("NAME",StringType,true)
    ,StructField("SSSS",StringType,true)
    )))))

    val df=inputstream
      .selectExpr("cast(value as string) as json")		//值转string再转json
      .select(from_json($"json",schema=schema).as("data")   )		//筛选json字段
      .select("data.data.*")
      
    df.createOrReplaceTempView("TempTable")

    val sqlText = "select * from TempTable" 
	
	//将所有符合条件的数筛出来加时间戳,再按1分钟统计
    val DF = spark
      .sql(sqlText)
      .withColumn("timestamp", functions.current_timestamp())	//增加列,方便按时间统计
      .groupBy(
        window($"timestamp", "1 minutes", "60 seconds")
      )
      .count()


    //====================输出到kafka=========================
   val outtopic = "OutTopic"
   val outbrokers = "OutBroker"
   
   val writer = new KafkaSink(outtopic, outbrokers)	
   //关于kafkasink的官方说明http://spark.apache.org/docs/latest/structured-streaming-kafka-integration.html
   
   val query = DF
    .writeStream
    .foreach(writer)
//    .outputMode("update")
//    .outputMode("append")
    .outputMode("complete")
    .trigger(ProcessingTime("10 seconds"))
    .start()
    query.awaitTermination()
  }
}
		
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!