6.3.1 Spark Streaming【DStream基础数据源、DStream转换操作、DStream输出操作、与kafka整合(08、010接口,offset偏移量)】

Spark Streaming



第1节 Spark Streaming概述

第2节 DStream基础数据源

基础数据源包括:文件数据流、socket数据流、RDD队列流;这些数据源主要用于测试。
在这里插入图片描述
引入依赖:

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

2.1 文件数据流

文件数据流:通过 textFileStream(directory) 方法进行读取 HDFS 兼容的文件系统文件
在这里插入图片描述
Spark Streaming 将会监控 directory 目录,并不断处理移动进来的文件

  • 不支持嵌套目录
  • 文件需要有相同的数据格式
  • 文件进入 directory 的方式需要通过移动或者重命名来实现
  • 一旦文件移动进目录,则不能再修改,即便修改了也不会读取新数据
  • 文件流不需要接收器(receiver),不需要单独分配CPU核
package cn.lagou.Streaming

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


object FileDSteam {
   
   
  def main(args: Array[String]): Unit = {
   
   
    //初始化
    Logger.getLogger("org").setLevel(Level.WARN)  //屏蔽日志
    val conf = new SparkConf().setAppName(this.getClass.getCanonicalName)
      .setMaster("local[*]")
    // 创建StreamingContext 
    // StreamingContext是所有流功能函数的主要访问点,这里使用多个执行线程和 10秒的批次间隔来创建本地的StreamingContext
    val ssc = new StreamingContext(conf, Seconds(10))   //设置10s启动一次

    //创建Dstream
    // 这里采用本地文件,也可以采用HDFS文件
    val lines: DStream[String] = ssc.textFileStream("data/log")

    //Dstream转换
    val words: DStream[String] = lines.flatMap(_.split("\\s+"))
    val result: DStream[(String, Int)] = words.map((_, 1)).reduceByKey(_ + _)

    //Dstream输出
    result.print(20)

    //启动作业
    ssc.start()
    ssc.awaitTermination()

  }
}

注:文件夹下只有新文件才会读取

2.2 Socket数据流

Spark Streaming可以通过Socket端口监听并接收数据,然后进行相应处理;

新开一个命令窗口,启动 nc 程序:

nc -lk 9999 
# yum install nc

随后可以在nc窗口中随意输入一些单词,监听窗口会自动获得单词数据流信息,在监听窗口每隔x秒就会打印出词频统计信息,可以在屏幕上出现结果。

备注:使用local[*],可能存在问题。
如果给虚拟机配置的cpu数为1,使用local[*]也只会启动一个线程,该线程用于receiver task,此时没有资源处理接收达到的数据。
【现象:程序正常执行,不会打印时间戳,屏幕上也不会有其他有效信息】
在这里插入图片描述
注意:DStream的 StorageLevel 是 MEMORY_AND_DISK_SER_2;

package cn.lagou.Streaming

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


object SocketDStream{
   
   
  def main(args: Array[String]): Unit = {
   
   
    //初始化
    Logger.getLogger("org").setLevel(Level.WARN)  //屏蔽日志
    val conf = new SparkConf().setAppName(this.getClass.getCanonicalName)
      .setMaster("local[*]")
    // 创建StreamingContext
    // StreamingContext是所有流功能函数的主要访问点,这里使用多个执行线程和 10秒的批次间隔来创建本地的StreamingContext
    val ssc = new StreamingContext(conf, Seconds(10))   //设置10s启动一次

    //创建Dstream
    // 这里采用本地文件,也可以采用HDFS文件
    //val lines: DStream[String] = ssc.textFileStream("data/log")
    val lines = ssc.socketTextStream("linux122", 9999)

    //Dstream转换
    val words: DStream[String] = lines.flatMap(_.split("\\s+"))
    val result: DStream[(String, Int)] = words.map((_, 1)).reduceByKey(_ + _)

    //Dstream输出
    result.print(20)

    //启动作业
    ssc.start()
    ssc.awaitTermination()

  }
}

在这里插入图片描述
在这里插入图片描述

SocketServer程序(单线程),监听本机指定端口,与socket连接后可发送信息:

package cn.lagou.Streaming

import java.io.PrintWriter
import java.net.{
   
   ServerSocket, Socket}
import scala.util.Random

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

    val words: Array[String] = "Hello World Hello Hadoop Hellospark kafka hive zookeeper hbase flume sqoop".split(" \\ s + ")
    val n: Int = words.length
    val port: Int = 9999
    val random: Random = scala.util.Random
    val server = new ServerSocket(port)
    val socket: Socket = server.accept()
    println("成功连接到本地主机:" + socket.getInetAddress)
    while (true) {
   
   
      val out = new PrintWriter(socket.getOutputStream)
      out.println(words(random.nextInt(n)) + " " +
        words(random.nextInt(n)))
      out.flush()
      Thread.sleep(100)
    }

  }
}

将之前SocketDStream里面,
val lines = ssc.socketTextStream(“linux122”, 9999)
改为本都服务
val lines = ssc.socketTextStream(“localhost”, 9999)

先启动SocketLikeNC,再启动SocketDStream
在这里插入图片描述

SocketServer程序(多线程)

package cn.lagou.Streaming

import java.net.ServerSocket

object SocketServer {
   
   
  def main(args: Array[String]): Unit = {
   
   
    val server = new ServerSocket(9999)
    println(s"Socket Server 已启动: ${server.getInetAddress}:${server.getLocalPort}")
    while (true) {
   
   
      val socket = server.accept()
      println("成功连接到本地主机:" + socket.getInetAddress)
      new ServerThread(socket).start()
    }
  }
}

package cn.lagou.Streaming

import java.io.DataOutputStream
import java.net.Socket
class ServerThread(sock: Socket) extends Thread {
   
   
  val words = "hello world hello spark hello word hello java hello hadoop hello kafka"
  .split("\\s+")
  val length = words.length
  override def run(): Unit = {
   
   
    val out = new DataOutputStream(sock.getOutputStream)
    val random = scala.util.Random
    while (true) {
   
   
      val (wordx, wordy) = (words(random.nextInt(length)), words(random.nextInt(length)))
      out.writeUTF(s"$wordx $wordy")
      Thread.sleep(100)
    }
  }
}

2.3 RDD队列流

调试Spark Streaming应用程序的时候,可使用streamingContext.queueStream(queueOfRDD) 创建基于RDD队列的DStream;
在这里插入图片描述
备注:

  • oneAtATime:缺省为true,一次处理一个RDD;设为false,一次处理全部RDD
  • RDD队列流可以使用local[1]
  • 涉及到同时出队和入队操作,所以要做同步

例:每秒创建一个RDD(RDD存放1-100的整数),Streaming每隔1秒就对数据进行处理,计算RDD中数据除10取余的个数。

package cn.lagou.Streaming

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 scala.collection.mutable


object RDDQueueDStream {
   
   
  def main(args: Array[String]): Unit = {
   
   
    Logger.getLogger("org").setLevel(Level.WARN)
    val sparkConf = new
        SparkConf().setAppName(this.getClass.getCanonicalName).setMaster(
      "local[2]")
    // 每隔1秒对数据进行处理
    val ssc = new StreamingContext(sparkConf, Seconds(1))

    //创建DStream
    val rddQueue = new mutable.Queue[RDD[Int]]()
    val queueStream = ssc.queueStream(rddQueue) //从队列中拿到DS
    val mappedStream = queueStream.map(r => (r % 10, 1)) //DS中每个元素除10取余,做成key\value对
    val reducedStream = mappedStream.reduceByKey(_ + _)
    reducedStream.print()

    ssc.start()
    // 每秒产生一个RDD,将RDD放置在队列中
    for (i <- 1 to 5){
   
   
      rddQueue.synchronized {
   
   
        val range = (1 to 100).map(_*i)
        rddQueue += ssc.sparkContext.makeRDD(range, 2)  //2 代表两个分区
      }
      Thread.sleep(2000)
    }
    ssc.stop()
  }
}

在这里插入图片描述

第3节 DStream转换操作

DStream上的操作与RDD的类似,分为 Transformations(转换)和 Output Operations(输出)两种,此外转换操作中还有一些比较特殊的方法,如:
updateStateByKey、transform 以及各种 Window 相关的操作。
在这里插入图片描述
在这里插入图片描述
备注:

  • 在DStream与RDD上的转换操作非常类似(无状态的操作)
  • DStream有自己特殊的操作(窗口操作、追踪状态变化操作)
  • 在DStream上的转换操作比RDD上的转换操作少

DStream 的转化操作可以分为 无状态(stateless) 和 有状态(stateful) 两种:

  • 无状态转化操作。每个批次的处理不依赖于之前批次的数据。常见的 RDD 转化操作,例如 map、filter、reduceByKey 等
  • 有状态转化操作。需要使用之前批次的数据 或者是 中间结果来计算当前批次的数据。有状态转化操作包括:基于滑动窗口的转化操作 或 追踪状态变化的转化操作

3.1 无状态转换

无状态转化操作就是把简单的 RDD 转化操作应用到每个批次上,也就是转化DStream 中的每一个 RDD。

常见的无状态转换包括:map、flatMap、filter、repartition、reduceByKey、groupByKey;直接作用在DStream上

重要的转换操作:transform。通过对源DStream的每个RDD应用RDD-to-RDD函数,创建一个新的DStream。支持在新的DStream中做任何RDD操作。
在这里插入图片描述
这是一个功能强大的函数,它可以允许开发者直接操作其内部的RDD。也就是说开发者,可以提供任意一个RDD到RDD的函数,这个函数在数据流每个批次中都被调用,生成一个新的流。

示例:黑名单过滤

假设:arr1为黑名单数据(自定义),true表示数据生效,需要被过滤掉;false表示数据未生效
val arr1 = Array((“spark”, true), (“scala”, false))
假设:流式数据格式为"time word",需要根据黑名单中的数据对流式数据执行过滤操作。如"2 spark"要被过滤掉
1 hadoop
2 spark
3 scala
4 java
5 hive
结果:“2 spark” 被过滤

方法一:使用外连接

package cn.lagou.Streaming.basic

import org.apache.log4j.{
   
   Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值