SparkStreaming

1 Spark Streaming 概述

  • 实时计算
  • 可以构建可扩展的容错流应用程序

1.1 什么是 Spark Streaming

  • Spark Streaming 类似于 Apache Storm,用于流式数据的处理。根据其官方文档介绍, SparkStreaming 有高吞吐量和容错能力强等特点。Spark Streaming 支持的数据输入源很多,例如: Kafka、Flume、 Twitter、 ZeroMQ 和简单的 TCP 套接字等等。数据输入后可以用 Spark 的高度抽象原语。如: map、 reduce、 join、 window 等进行运算。而结果也能保存在很多地方,如 HDFS,数据库等。另外 Spark Streaming 也能和 MLlib(机器学习)以及 Graphx 完美融合。
  • 官网
  • 在这里插入图片描述
  • 和 Spark 基于 RDD 的概念很相似, Spark Streaming 使用离散化流(discretized stream)作为抽象表示,叫作 DStream。 DStream 是随时间推移而收到的数据的序列。在内部,每个时间区间收到的数据都作为 RDD 存在,而 DStream 是由这些 RDD 所组成的序列(因此 得名“离散化”)。
  • 在这里插入图片描述
  • DStream 可以从各种输入源创建,比如 Flume、 Kafka 或者 HDFS。创建出来的 DStream 支持两种操作,一种是转化操作(transformation),会生成一个新的 DStream,另一种是输出操作(output operation),可以把数据写入外部系统中。 DStream 提供了许多与 RDD 所支持的操作相类似的操作支持,还增加了与时间相关的新操作,比如滑动窗口。

1.2 Spark Streaming的特点

  • 易用
  • 容错
  • 易整合到 Spark 体系

1.3 Spark 与 Storm 的对比

-SparkStorm
开发语言ScalaClojure
编程模型DStreamSpout/Bolt

2 架构与抽象

2.1 整体架构

  • Spark Streaming 使用“微批次”的架构,把流式计算当作一系列连续的小规模批处理来对待。
  • 在这里插入图片描述
  • Spark Streaming 从各种输入源中读取数据,并把数据分组为小的批次。新的批次按均匀的时间间隔创建出来。在每个时间区间开始的时候,一个新的批次就创建出来,在该区间内收到的数据都会被添加到这个批次中。在时间区间结束时,批次停止增长。时间区间的大小是由批次间隔这个参数决定的。批次间隔一般设在 500 毫秒到几秒之间,由应用开发者配置。每个输入批次都形成一个 RDD,以 Spark 作业的方式处理并生成其他的 RDD。 处理的结果可以以批处理的方式传给外部系统。高层次的架构如图
  • 在这里插入图片描述
  • Spark Streaming 的编程抽象是离散化流,也就是 DStream。 它是一个 RDD 序列,每个 RDD代表数据流中一个时间片内的数据
  • 在这里插入图片描述

2.2 DStreams 抽象概念

  • 把流进行抽象处理,把数据流按时间片为单位划分为一部分一部分,分别抽象为RDD -> 伪实时
  • 其中每个RDD都会基于内存的快速计算。
  • 按照时间为界限把数据流切分为小单元->小数据片,每个单元使用RDD来处理。每个小单元都是按照时间维度的批次数据。微批次架构。面向每一个小批次的架构。
  • 最后是否整合还要看具体业务需求。
  • 有少量延迟。
  • 使用Scala开发
  • 批次间隔设置为500ms到几秒之间,可以自行配置。
  • 【Discretixed :离散的】
  • kafka:分布式分区负载均衡;副本可以有容错率;吞吐量高;默认保存7天的数据。
  • Discretized Stream 是 Spark Streaming 的基础抽象,代表持续性的数据流和经过各种 Spark 原语操作后的结果数据流。
  • 在这里插入图片描述
  • DStream : Discretized Stream 离散化流
  • 在内部实现上, DStream 是一系列连续的 RDD 来表示。每个 RDD 含有一段时间间隔内的数据,如下图:
  • 在这里插入图片描述
  • 对数据的操作也是按照 RDD 为单位来进行的
  • 在这里插入图片描述
  • 计算过程由 Spark engine 来完成
  • 在这里插入图片描述
  • Spark Streaming 在 Spark 的驱动器程序—工作节点的结构的执行过程如下图所示。 SparkStreaming 为每个输入源启动对 应的接收器。接收器以任务的形式运行在应用的执行器进程中,从输入源收集数据并保存为 RDD。它们收集到输入数据后会把数据复制到另一个执行器进程来保障容错性(默 认行为)。数据保存在执行器进程的内存中,和缓存 RDD 的方式一样。驱动器程序中的 StreamingContext 会周期性地运行 Spark 作业来处理这些数据,把数据与之前时间区间中的 RDD 进行整合

3 运行Spark Streaming

3.1 IDEA 编写程序

  • Pom.xml 加入以下依赖:
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>${spark.version}</version>
<scope>provided</scope>
</dependency
 //至少需要两个线程
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("hello Streaming")

    //指定 5s 一批次 微批次的计算模式
    val streamingContext = new StreamingContext(sparkConf,Seconds(5))

    val dStream:DStream[String] = streamingContext.socketTextStream("node102",54321)

    //业务线维度:
    val flatMapDStream = dStream.flatMap(_.split(" "))
    val mapDStream = flatMapDStream.map((_,1))
    val reduceDStream = mapDStream.reduceByKey(_+_)
	reduceDStream.print()
    reduceDStream.foreachRDD(_.foreach(println))

    streamingContext.start()  //开启这个任务
    streamingContext.awaitTermination() //等待
  • Linux下nc命令的使用
    nc命令的作用
    1. 实现任意TCP/UDP端口的侦听,nc可以作为server以TCP或UDP方式侦听指定端口
    2. 端口的扫描,nc可以作为client发起TCP或UDP连接
    3. 机器之间传输文件
    4. 机器之间网络测速
[bduser@node102 ~]$ nc -lk 54321
li zh en li test soa test spark soa
a
a a a a
b
b
b
bb
b

bb bb  b bb b

-------------------------------------------
Time: 1584599230000 ms
-------------------------------------------
(,1)
(bb,1)
(a,5)
(b,4)

(,1)
(bb,1)
(a,5)
(b,4)
-------------------------------------------
Time: 1584599235000 ms
-------------------------------------------
(,1)
(bb,3)
(b,2)

(,1)
(bb,3)
(b,2)
-------------------------------------------
Time: 1584599240000 ms
---------------
  • 无状态转换 每个批次之间的数据计算以及 数据结果互相不受影响。
  • 无状态转化操作就是把简单的 RDD 转化操作应用到每个批次上,也就是转化 DStream 中的每一个 RDD。部分无状态转化操作列在了下表中。 注意,针对键值对的 DStream 转化操作(比如 reduceByKey())要添加 import StreamingContext._ 才能在 Scala 中使用。
  • 在这里插入图片描述
  • 需要记住的是,尽管这些函数看起来像作用在整个流上一样,但事实上每个 DStream 在内部是由许多 RDD(批次)组成,且无状态转化操作是分别应用到每个 RDD 上的。例如,reduceByKey() 会归约每个时间区间中的数据,但不会归约不同区间之间的数据。
  • 举个例子,在之前的 wordcount 程序中, 我们只会统计 1 秒内接收到的数据的单词个数,而不会累加。
  • 无状态转化操作也能在多个 DStream 间整合数据,不过也是在各个时间区间内。例如,键 值对 DStream 拥有和 RDD 一样的与连接相关的转化操作 ,也就是 cogroup()、 join()、leftOuterJoin() 等。我们可以在 DStream 上使用这些操作,这样就对每个批次分别执行了对应的RDD 操作
  • 我们还可以像在常规的 Spark 中一样使用 DStream 的 union() 操作将它和另一个DStream 的内容合并起来,也可以使用 StreamingContext.union() 来合并多个流。
  • 无状态转换的tranform算子
    1. 在Driver执行
    2. rdd.map 又是一个Driver
    3. v => (v,1) 是Executer负责执行
    4. 动态数据变更的操作处理可以写在rdd =>之后执行
windowDStream.transform((rdd) => rdd.map((v) => (v,1)))

3.2 自定义接收器

  • 通过继承 Receiver,并实现 onStart、 onStop 方法来自定义数据源采集。
class MySocketReceiver(hostname:String,port:Int) extends Receiver[String](StorageLevel.MEMORY_ONLY){
  override def onStart(): Unit = {
    new Thread{
      override def run() = {
        var socket: Socket = null
        var inputStr:String = null
        try{
          socket = new Socket(hostname,port)
          val bufferReader = new BufferedReader(new InputStreamReader(socket.getInputStream,"UTF-8"))

          while(isStarted && (inputStr = bufferReader.readLine()) != null){
            store(inputStr)
          }

          restart("没数据的了,但是不能停,重启接收服务")
        }catch {
          case e:ConnectException => println("连接失败");restart("连接中断,重启接入服务")
          case _ => restart("未知错误,重启接受服务")
        }
      }
    }.start()
  }

  override def onStop(): Unit = {

  }
}
  • 可以通过 streamingContext.receiverStream(<instance of custom receiver>)来使用自定义的数据采集源
    //至少需要两个线程
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("hello Streaming")

    //指定 5s 一批次 微批次的计算模式
    val streamingContext = new StreamingContext(sparkConf,Seconds(5))
                              //文本接收器
    //val dStream:DStream[String] = streamingContext.socketTextStream("node102",54321)

    val dStream = streamingContext.receiverStream(new MySocketReceiver("node102",12345))

    //业务线维度:
    val flatMapDStream = dStream.flatMap(_.split(" "))
    val mapDStream = flatMapDStream.map((_,1))
    val reduceDStream = mapDStream.reduceByKey(_+_)

    reduceDStream.print()
    reduceDStream.foreachRDD(_.foreach(println))

    streamingContext.start()  //开启这个任务
    streamingContext.awaitTermination() //等待
[bduser@node102 ~]$ nc -lk 12345
a a a a

a
a
 a
a
 a a a a a
 a
a
a

-------------------------------------------
Time: 1584603280000 ms
-------------------------------------------

-------------------------------------------
Time: 1584603285000 ms
-------------------------------------------
(,3)
(a,13)

3.3 基本数据源

文件数据源

  • Socket 数据流前面的例子已经看到过。
  • 文件数据流:能够读取所有 HDFS API 兼容的文件系统文件,通过 fileStream 方法进行读取
  • Spark Streaming 将会监控 dataDirectory 目录并不断处理移动进来的文件,记住目前不支持嵌套目录。
  • 文件需要有相同的数据格式
  • 文件进入 dataDirectory 的方式需要通过移动或者重命名来实现。
  • 一旦文件移动进目录,则不能再修改,即便修改了也不会读取新数据。
  • 如果文件比较简单,则可以使用 streamingContext.textFileStream(dataDirectory)方法来读取文件。文件流不需要接收器,不需要单独分配 CPU 核。
  • Hdfs 读取实例:
  • 提前需要在 HDFS 上建好目录。

3.4 RDD 队列

  • 测试过程中,可以通过使用 streamingContext.queueStream(queueOfRDDs)来创建 DStream,每一个推送到这个队列中的 RDD,都会作为一个 DStream 处理。

3.5 高级数据源

  • 除核心数据源外,还可以用附加数据源接收器来从一些知名数据获取系统中接收的数据,这些接收器都作为 Spark Streaming 的组件进行独立打包了。它们仍然是 Spark 的一部分,不过你需要在构建文件中添加额外的包才能使用它们。现有的接收器包括 Twitter、 Apache Kafka、 AmazonKinesis、 Apache Flume,以及 ZeroMQ。可以通过添加与 Spark 版本匹配 的 Maven 工件spark-streaming-[projectname]_2.10 来引入这些附加接收器。

4 Spark Streaming 解析

  • 初始化完 Context 之后:
  1. 定义消息输入源来创建 DStreams.
  2. 定义 DStreams 的转化操作和输出操作。
  3. 通过 streamingContext.start()来启动消息采集和处理.
  4. 等待程序终止,可以通过 streamingContext.awaitTermination()来设置
  5. 通过 streamingContext.stop()来手动终止处理程序。
  • StreamingContext 和 SparkContext 什么关系?
    1. StreamingContext 一旦启动,对 DStreams 的操作就不能修改了。
    2. 在同一时间一个 JVM 中只有一个 StreamingContext 可以启动
    3. stop() 方 法 将 同 时 停 止 SparkContext , 可 以 传 入 参 数 stopSparkContext 用 于 只 停 止StreamingContext
    4. 在 Spark1.4 版 本 后 , 如 何 优 雅 的 停 止 SparkStreaming 而 不 丢 失 数 据 , 通 过 设 置sparkConf.set("spark.streaming.stopGracefullyOnShutdown","true") 即可。在 StreamingContext的 start方法中已经注册了 Hook 方法。
  • 用于SparkCore组件的伪实时的分布式计算组件
  • 采用微批次的计算架构
  • 抽象对应:
    1. SparkCore -> RDD
    2. SparkSQL -> DataFrame | DataSet
    3. SparkStreaming -> DStream(离散化流)
  • SparkCore->SparkContext(SparkConf)
  • SparkSQL->SparkSession(SparkContext)
  • SparkStreaming->StreamingContext(SparkConf, Duration(millstime))
  • SparkStreaming的执行进程:
    Driver进程
    Executer进程
    Receiver接收器进程(一直在运行的进程)

4.1 SparkStreaming中的算子

  • 利用RDD 的原语算子进行包装
    DStream.flatMap.map.reduceByKey
  • 无状态转换
  • 有状态转换
    1. 普通有状态转换算子(必须有检查点机制的支持) 特殊的 Transformations
    2. 窗口函数

4.1.1 普通有状态转换算子

  • UpdateStateByKey 原语用于记录历史记录,有时,我们需要在 DStream 中跨批次维护状态(例如流计算中累加 wordcount)。针对这种情况, updateStateByKey() 为我们提供了对一个状态变量的访问,用于键值对形式的 DStream。给定一个由(键,事件)对构成的 DStream,并传递一个指定如何根据新的事件 更新每个键对应状态的函数,它可以构建出一个新的 DStream,其内部数据为(键,状态) 对。
  • updateStateByKey() 的结果会是一个新的 DStream,其内部的 RDD 序列是由每个时间区间对应的(键,状态)对组成的。
  • updateStateByKey 操作使得我们可以在用新信息进行更新时保持任意的状态。为使用这个功能,你需要做下面两步:
    1. 定义状态,状态可以是一个任意的数据类型。
    2. 定义状态更新函数,用此函数阐明如何使用之前的状态和来自输入流的新值对状态进行更新。
  • 使用 updateStateByKey 需要对检查点目录进行配置,会使用检查点来保存状态。
updateStateByKey((Seq,Option)=>Option)
//至少需要两个线程
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("hello Streaming")

    //指定 5s 一批次 微批次的计算模式
    val streamingContext = new StreamingContext(sparkConf,Seconds(5))
                              //文本接收器
    val dStream:DStream[String] = streamingContext.socketTextStream("node102",54321)

    //val dStream = streamingContext.receiverStream(new MySocketReceiver("node102",12345))

    //为有状态转换算子设置检查点的目录位置
    streamingContext.sparkContext.setCheckpointDir("./chkdir")


    //业务线维度:
    val flatMapDStream = dStream.flatMap(_.split(" "))
    val mapDStream = flatMapDStream.map((_,1))
    //无状态转换:
    //val reduceDStream = mapDStream.reduceByKey(_+_)
    //reduceDStream.print()

    //有状态转换
    val updateStateDStream = mapDStream.updateStateByKey{
      (seq:Seq[Int],state:Option[Int]) =>
        val result = state.getOrElse(0) + seq.sum
        Some(result)
    }
    //简写:
    //mapDStream.updateStateByKey((seq:Seq[Int],state:Option[Int]) => Some(seq.sum + state.getOrElse(0)))
    updateStateDStream.print()
    updateStateDStream.foreachRDD(_.foreach(println))

    streamingContext.start()  //开启这个任务
    streamingContext.awaitTermination() //等待

 a
 aa
 a a
 a
a
a
 a aaaa
aa
a

-------------------------------------------
Time: 1584621695000 ms
-------------------------------------------
(aa,2)
(aaaa,1)
(,11)
(a,18)

(aa,2)
(aaaa,1)
(,11)
(a,18)
-------------------------------------------
Time: 1584621700000 ms
-------------------------------------------
(aa,2)
(aaaa,1)
(,11)
(a,18)

(aa,2)
(aaaa,1)
(,11)
(a,18)

4.1.2 窗口函数

  • window(窗口长度[Duration[millstime]], 步长[Duration[millstime]])
  • reduceByWindow
  • countByWindow
  • reduceByKeyAndWindow
  • countByKeyAndWindow
  • Window Operations 有点类似于 Storm 中的 State,可以设置窗口的大小和滑动窗口的间隔来动态的获取当前 Steaming 的允许状态
  • 基于窗口的操作会在一个比 StreamingContext 的批次间隔更长的时间范围内,通过整合多个批次的结果,计算出整个窗口的结果
  • 所有基于窗口的操作都需要两个参数,分别为窗口时长以及滑动步长,两者都必须是StreamContext 的批次间隔的整数倍。窗口时长控制每次计算最近的多少个批次的数据,其实就是最近的 windowDuration/batchInterval 个批次。如果有一个以 10 秒为批次间隔的源 DStream,要创建一个最近 30 秒的时间窗口(即最近 3 个批次),就应当把 windowDuration 设为 30 秒。而滑动步长的默认值与批次间隔相等,用来控制对新的 DStream 进行计算的间隔。如果源DStream 批次间隔为 10 秒,并且我们只希望每两个批次计算一次窗口结果, 就应该把滑动步长设置为 20 秒
  • 假设,你想拓展前例从而每隔十秒对持续 30 秒的数据生成 word count。为做到这个,我们需要在持续 30秒数据的(word,1)对 DStream上应用 reduceByKey。使用操作 reduceByKeyAndWindow.
  • 在这里插入图片描述
  • SQL体系的开窗函数:
		select emp.*,rank() over(parition by deptno order by sal desc) from emp;
    val list = List(1,2,3,4,5,6,7)

    //sliding :滑动
    val iterator = list.sliding(3,2)//第一个参数是窗口长度;第二个是步长

    for (elem <- iterator) {
      println(elem)
    }





    //至少需要两个线程
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("hello Streaming")

    //指定 5s 一批次 微批次的计算模式
    val streamingContext = new StreamingContext(sparkConf,Seconds(5))
    //文本接收器
    val dStream:DStream[String] = streamingContext.socketTextStream("node102",54321)

    val windowDStream = dStream.window(Seconds(15),Seconds(10))

    //业务线维度:
    val flatMapDStream = windowDStream.flatMap(_.split(" "))
    val mapDStream = flatMapDStream.map((_,1))

    //s是总和 v是后来的数据    窗口长度 步长
//    val reduceDStream = mapDStream.reduceByKeyAndWindow((s:Int,v:Int) => s + v,Seconds(15),Seconds(10))
//    reduceDStream.print()

    //无状态转换:
    val reduceDStream = mapDStream.reduceByKey(_+_)
    reduceDStream.print()

    streamingContext.start()  //开启这个任务
    streamingContext.awaitTermination() //等待

由于val windowDStream = dStream.window(Seconds(15),Seconds(10))配置,窗口长度变成10m。

List(1, 2, 3)
List(3, 4, 5)
List(5, 6, 7)
-------------------------------------------
Time: 1584625410000 ms
-------------------------------------------

                                                                                -------------------------------------------
Time: 1584625420000 ms
-------------------------------------------
(aa,1)
(,2)
(a,13)

4.2 DStream的输出

  • 输出操作指定了对流数据经转化操作得到的数据所要执行的操作(例如把结果推入外部数据库或输出到屏幕上)。与 RDD 中的惰性求值类似,如果一个 DStream 及其派生出的 DStream 都没有被执行输出操作,那么这些 DStream 就都不会被求值。如果 StreamingContext 中没有设定输出操作,整个 context 就都不会启动。
Output OperationMeaning
print()在运行流程序的驱动结点上打印DStream 中每一批次数据的最开始 10个 元 素 。 这 用 于 开 发 和 调 试 。 在Python API 中 , 同 样 的 操 作 叫pprint()。
saveAsTextFiles(prefix, [suffix])以 text 文 件 形 式 存 储 这 个DStream 的内容。每一批次的存储文件名基于参数中的 prefix 和 suffix。”prefix-Time_IN_MS[.suffix]”.
saveAsObjectFiles(prefix, [suffix])以 Java 对 象 序 列 化 的 方 式 将Stream 中 的 数 据 保 存 为SequenceFiles . 每 一 批 次 的 存 储 文件 名 基 于 参 数 中 的 为"prefix-TIME_IN_MS[.suffix]". Python中目前不可用。
saveAsHadoopFiles(prefix, [suffix])将 Stream 中 的 数 据 保 存 为Hadoop files. 每一批次的存储文件名 基 于 参 数 中 的 为"prefix-TIME_IN_MS[.suffix]".Python API Python 中目前不可用。
foreachRDD(func)这是最通用的输出操作,即将函数 func 用于产生于 stream 的每一个RDD。其中参数传入的函数 func 应该实现将每一个 RDD 中数据推送到外部系统,如将 RDD 存入文件或者通过网络将其写入数据库。注意:函数 func在运行流应用的驱动中被执行,同时其中一般函数 RDD 操作从而强制其对于流 RDD 的运算。
  • 通用的输出操作 foreachRDD(),它用来对 DStream 中的 RDD 运行任意计算。这和transform() 有些类似,都可以让我们访问任意 RDD。在 foreachRDD() 中,可以重用我们在 Spark中实现的所有行动操作。比如,常见的用例之一是把数据写到诸如 MySQL 的外部数据库中
  • 需要注意的:
    1. 连接不能写在 driver 层面
    2. 如果写在 foreach 则每个 RDD 都创建,得不偿失
    3. 增加 foreachPartition,在分区创建
    4. 可以考虑使用连接池优化

4.2.1 kafka

  • 加入依赖
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>0.11.0.3</version>
        </dependency>
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Hello Streaming")
    val streamingContext = new StreamingContext(sparkConf,Seconds(5))

    val kafkaParams = Map[String, Object](
      "bootstrap.servers" -> "node102:9092,node103:9092",
      ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer",
      "value.deserializer" -> classOf[StringDeserializer],
      "group.id" -> "my_spark_kafka_consumer",
      "auto.offset.reset" -> "latest",
      "enable.auto.commit" -> "false" //SparkStreaming可以自动提交
    )

    val topics = Array("sparkstreamingused")
    val kafkaDStream = KafkaUtils.createDirectStream[String, String](
      streamingContext,
      PreferConsistent,
      Subscribe[String, String](topics, kafkaParams)
    )

    kafkaDStream.map(record => (record.value,1)).reduceByKey(_+_).print()

    streamingContext.start()
    streamingContext.awaitTermination()
[bduser@node102 ~]$ kafka-topics.sh --zookeeper node102:2181 --partitions 3 --replication-factor 2 --topic sparkstreamingused --create
[bduser@node102 ~]$ kafka-console-producer.sh --broker-list  node102:9092 --topic sparkstreamingused
>a
a>
>a
>a
>a
>a
>a
>a
>a
>a
>a
>a
>a
>
>aa
>a
>a
>a
>a

                                                                                -------------------------------------------
Time: 1584671865000 ms
-------------------------------------------

-------------------------------------------
Time: 1584671870000 ms
-------------------------------------------

-------------------------------------------
Time: 1584671875000 ms
-------------------------------------------

-------------------------------------------
Time: 1584671880000 ms
-------------------------------------------

                                                                                -------------------------------------------
Time: 1584671885000 ms
-------------------------------------------
(aa,1)
(,1)
(a,16)
(a ,1)

4.2.2 Flume

[bduser@node102 ~]$ vim flumespark.conf

a1.sources = r1
a1.sinks = k1
a1.channels = c1

# Describe/configure the source
a1.sources.r1.type = netcat
a1.sources.r1.bind = node102
a1.sources.r1.port = 44444

# Describe the sink
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = node102
a1.sinks.k1.port = 12345

# Use a channel which buffers events in memory
a1.channels.c1.type = memory
a1.channels.c1.capacity = 100000
a1.channels.c1.transactionCapacity = 1000

# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
  • 配置依赖
<dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming-flume_2.11</artifactId>
            <version>2.1.3</version>
       </dependency>
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Hello Streaming")
    val streamingContext = new StreamingContext(sparkConf,Seconds(5))

    val flumeDStream = FlumeUtils.createStream(streamingContext, "node102", 12345)

    flumeDStream.map(e => (e.event.getBody,1)).reduceByKey(_+_).print()

    streamingContext.start()
    streamingContext.awaitTermination()
[bduser@node102 ~]$ flume-ng agent --conf /opt/modules/flume-1.9.0/conf -n a1 -f flumespark.conf

[bduser@node102 ~]$ nc node102 44444

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值