spark-streaming

streaming

数据处理延迟的长短,实时和离线处理区别

实时数据处理:毫秒级别

离线数据处理:小时 or 天级别

Sparkstreaming 是一个准实时(秒,分钟),微批次(时间)的数据处理框架

指定间隔来进行 采集数据进行执行

scala采集器,3秒间隔接收,同时间段的都会被采集

  def main(args: Array[String]): Unit = {
    val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
    val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
    val value: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 9999)
    val words: DStream[String] = value.flatMap(_.split(" "))
    val wordToOne: DStream[(String, Int)] = words.map((_, 1))
    val wordCount: DStream[(String, Int)] = wordToOne.reduceByKey(_ + _)
    wordCount.print()
    // sparkStreaming采集器是不可以直接就关闭
    // 如果main方法执行完毕,应用程序也会自动结束,所以也不能让main结束
    //    ssc.stop()
    // 1.启动采集器
    ssc.start()
    // 2.等待采集器的关闭
    ssc.awaitTermination()
  }

自定义数据采集器

  def main(args: Array[String]): Unit = {
    val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
    val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
    val value: ReceiverInputDStream[String] = ssc.receiverStream(new MyReceiver())
    value.print()
    ssc.start()
    ssc.awaitTermination()
  }
  class MyReceiver extends Receiver[String](StorageLevel.MEMORY_ONLY){
    private var flg = true
    override def onStart(): Unit = {
      new Thread(new Runnable {
        override def run(): Unit = {
          while(flg){
            val message = "采集的数据为:" + new Random().nextInt(10).toString
            store(message)
            Thread.sleep(500)
          }
        }
      }).start()
    }

    override def onStop(): Unit = {
      flg=false
    }
  }

读取kafka数据

spark-streaming一般都是和kafka来进行工作

首先我们需要集群启动kafka

bin/kafka-topics.sh --bootstrap-server master:9092 --list
# create 创建  topic创建top的名称  partitions分区数  replication-factor复制因子数量
bin/kafka-topics.sh --bootstrap-server master:9092 --create --topic cdl --partitions 3 --replication-factor 1
#启动topic
bin/kafka-console-producer.sh --broker-list master:9092 --topic cdl

这边kafka我只启动了一个

  def main(args: Array[String]): Unit = {
    val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
    val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
    val kafkaPara: Map[String, Object] = Map[String, Object](
      ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> "master:9092",
      ConsumerConfig.GROUP_ID_CONFIG -> "cdl",
      "key.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer",
      "value.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer"
    )

    val kafkaDataDS: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String, String](
      ssc,
      LocationStrategies.PreferConsistent,
      ConsumerStrategies.Subscribe[String, String](Set("cdl"), kafkaPara)
    )
    kafkaDataDS.map(_.value()).print()
    ssc.start()
    ssc.awaitTermination()
  }

然后再启动的topic下输入任意字符,查看控制台接收到即可成功

状态操作

进行map,groupbykey等操作都是无状态操作,只针对当前周期内的数据进行计算,过了之前的就没了

如果并保存了操作就是有状态操作

有状态操作

  def main(args: Array[String]): Unit = {
    val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
    val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
    ssc.checkpoint("cp")// 指定检查点落地路径
    val value: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 9999)
    val v: DStream[(String, Int)] = value.map((_, 1))
    //使用有状态操作时候需要指定检查点路径
    // updateStateByKey:根据key数据的状态进行更新
    // 两个参数
    // 第一个值表示相同key的value数据
    // 第二个值表示缓存区相同key的数据
    v.updateStateByKey(
      (seq:Seq[Int],buff:Option[Int])=>{
        val newCount = buff.getOrElse(0)+seq.sum
        Option(newCount)
      }
    ).print()
    ssc.start()
    ssc.awaitTermination()
  }

无状态操作transform

transform可以直接获取到底层的rdd,和上面直接用map操作不同的是,transform可以周期性的源源不断的

  def main(args: Array[String]): Unit = {
    val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
    val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
    ssc.checkpoint("cp")// 指定检查点落地路径
    val value: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 9999)
    value.transform(rdd=>rdd.map(rdd=>rdd))
    ssc.start()
    ssc.awaitTermination()
  }

无状态操作join

join可以把两个不同数据源的数据拼接在一起

有状态操作window

两个参数:窗口时长 , 滑动步长

窗口时长:计算内容的时间范围

滑动步长:隔多久触发一次计算

且必须要是采集周期的整数倍数

  def main(args: Array[String]): Unit = {
    val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
    val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
    ssc.checkpoint("cp")// 指定检查点落地路径
    val value: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 9999)
    val value1: DStream[(String, Int)] = value.map((_, 1))
    // 传入的窗口时长和滑动步长相同的时候,就可以避免重复数据
    // 而且都必须是上方ssc的采集周期的整数倍数
    val wd: DStream[(String, Int)] = value1.window(Seconds(6), Seconds(6))
    val rd: DStream[(String, Int)] = wd.reduceByKey(_ + _)
    ssc.start()
    ssc.awaitTermination()
  }

更多方法

window(windowLength,silidenterval):基于对源DStream窗口的批次进行计算返回一个新的Dstream

countByWindow(windowLength,silidenterval):返回一个滑动窗口计数流中的元素个数

reduceByWindow(func,windowLength,silidenterval):通过使用自定义函数整合滑动区间流元素来创建新的单元素流

reduceByKeyAndWindow(func,windowLength,silidenterval,[numTesks]):当在一个(K,V)对的DStream上调用此函数,会返回一个新(K,V)对的DStream,此处通过对滑动窗口中批次数据使用reduce函数来整合每个key的value值

reduceByKeyAndWindow(func,invFunc,windowLength,silidenterval,[numTesks]):此函数是上面函数的变化版本,多了一个invFUnc的参数,用来删掉不需要的数据,第一个参数用来传入进行计算数据

DStream输出

上方的操作都是有关DStream输入的操作,我们当中执行的print方法就是属于DStream输出的操作

在进行流输入的时候数据是源源不断输入的,在运行过程中 必须要有输出操作,否则就会报错

输出操作如下:

print():在运行流程序的驱动结点上答应DStream中每一批次数据的最开始10个元素,这用于开发调试

saveAsTextFiles(prefix,[suffix]):text文件形式存储,每一批次存储文件名指定prefix.suffix

saveAsObjectFiles(prefix,[suffix]):以java对象序列化的方式保存数据

saveAsHadoopFiles(prefix,[suffix]):保存成hadoop files

foreachRDD(func):这是最通用的输出操作,通过函数func对每一个rdd进行实现,将rdd推送到外部系统,存入数据库或者文件等,可以重用spark实现的所有行动操作,要注意:

1.连接不能卸载driver层面(序列化的原因)

2.如果写在foreach则每个rdd中的每一条数据都创建,得不偿失

3.增加foreachPartition,在分区创建获取

优雅关闭程序

在流运行的过程中,有时候需要关闭进行升级

ssc.stop(true) // 进行强制关闭

ssc.stop(true,true) // 进行优雅的关闭,且会执行完当前其他正在执行的任务

    // 通过第三方程序增加关闭状态
    new Thread(new Runnable {
      override def run(): Unit = {
        // 优雅的关闭
        // 我们可以通过以下方式
        // Mysql:
        // Redis:
        // ZK:
        // HDFS:
        // 获取状态后执行代码进行关闭
        val state = ssc.getState()
        if(state == StreamingContextState.ACTIVE){
          ssc.stop(true,true)
          System.exit(0) // 关闭线程
        }
      }
    })

当我们再运行过程之中指定了检查点的路径,再次启动的时候可以再次读取检查点的路径,如果读取不到就重新创建一个

val ssc = StreamingContext.getActiveOrCreate("cp",()=>{

    val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
    val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
    val value: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 9999)
    val value1: DStream[(String, Int)] = value.map((_, 1))
    value.print()
    ssc
})
ssc.checkpoint("cp") // 保存检查点
ssc.start()
ssc.awaitTermination()

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值