07-SparkStreaming

07-Spark Streaming

1.目录概述

  • 掌握spark Streaming的原理和架构

  • 掌握DStream的相关操作

  • 实现spark Streaming与flume整合

  • 实现spark Streaming与kafaka整合

2.spark Streaming介绍

2.1.什么是spark Streaming

spark Streaming类似于Apache Storm,用于流式数据处理。根据官方文档介绍,spark Streaming有高吞吐量容错能力强等特点。

spark Streaming支持多数据源。例如:Kafka、Flume、Twitter、ZeroMQ和简单的TCP套接字等。数据输入后可以用spark的高度抽象操作如:map、reduce、join、window等进行运算。结果可以存储保存在多个地方。如HDFS,数据库等。

在这里插入图片描述

2.2.为什么要学习spark Streaming

  • 易用

    可以像编写离线批处理一样去编写流式程序,支持java/scala/python语言。

  • 容错

    spark Streaming在没有额外代码和配置的情况下可以恢复丢失的工作。

  • 与spark体系容易整合

    都在spark体系下,流式处理、批处理、交互式查询结合容易。

2.3.spark Streaming 与storm对比

在这里插入图片描述

详细对比:

对比事项stormspark Streaming
实时性纯实时,有一条数据,处理一条数据准实时,把一个时间段内的数据收集依赖,再处理
延迟度毫秒级秒级
吞吐量相对低
事务机制支持完善支持,不够完善
健壮性/容错性非常强一般

应用场景选择:

1.对于像金融系统、股票系统,要求纯实时的金融交易和分析,不能接受1秒以上的延迟,选择storm更好

2.对于可靠性事务机制要求高,即要求数据处理完全精准,一条数据都不能多,也不能少,选择storm更好

3.对于系统实时要求没有那么高,可以接受秒级别的延迟,选择spark Streaming会更好,因为spark Streaming有更高的吞吐量(单位时间处理的数据量更多)

4.对于系统除了实时性因素,如果还需要考虑离线批处理、交互式查询等业务功能,选择spark Streaming会更好,因为spark Streaming更容易和spark体系中的其他模块整合(spark Core、spark sql)

3.spark Streaming原理

3.1.spark Streaming原理

spark Streaming 是基于spark的流式【批处理】引擎,基本原理是把输入数据以某一时间间隔进行批量处理。当批处理间隔缩短到秒级时,可以用于处理实时数据流。

细节:spark Streaming不是实时处理,而是准实时处理,有秒级别的延迟。

3.2.spark Streaming计算流程

在这里插入图片描述

1.输入数据流入spark Streaming

2.spark Streaming将输入数据按照batch size分成一段一段的数据,每一段数据都转换成RDD

3.将spark Streaming中对DStream的Transformation操作,转换成对RDD的Transformation操作。将RDD经过操作变成中间结果保存在内存中。根据业务需求可以对中间计算结果叠加或者存储到外部设备

3.3.spark Streaming容错性

对于流式计算来说,容错性至关重要。首先我们要明确spark中RDD的容错机制。每一个RDD都是一个不可变的分布式可重算的数据集,其记录着确定性的操作继承关系(lineage),只要输入数据是可容错的,那么任意一个RDD的分区(Partition)出错或不可用,都可以利用原始输入数据通过转换操作而重新算出的。

在这里插入图片描述

3.4.spark Streaming实时性

  • spark Streaming的最小batch size 选取在(0.5~2)秒之间,属于准实时。适合于可以接受秒级别延迟的业务需求场景
  • 对于实时性要求非常高,比如金融系统、股票分析系统,storm会是更好的选择(storm的延迟在100ms左右)

4.DStream

4.1.什么是DStream

DStream(Discretized Stream)是spark Streaming的基础抽象。代表持续性的数据流和经过各种spark算子操作后的结果数据流。内部实现上,DStream表示为一系列连续的RDD。每个RDD含有一段时间间隔内的数据,如下图:

在这里插入图片描述

数据操作按照RDD为单位来进行:

在这里插入图片描述

spark Streaming使用数据源产生的数据流创建DStream,或者在已有的DStream上通过算子操作来创建新的DStream。它的工作流程像下面的图所示一样,接受到实时数据后,给数据分批次,然后传给spark Engine处理最后生成该批次的结果。

在这里插入图片描述

5.DStream相关操作

5.1.Transformations on DStream

TransformationMeaning
map(func)对DStream中的各个元素进行func函数操作,然后返回一个新的DStream
flatMap(func)与map方法类似,只不过各个输入项可以被输出为零个或多个输出项
filter(func)过滤出所有函数func返回值为true的DStream元素并返回一个新的DStream
repartition(numPartitions)增加或减少DStream中的分区数,从而改变DStream的并行度
union(otherStream)将源DStream和输入参数为otherDStream的元素合并,并返回一个新的DStream
count()通过对DStream中的各个RDD中的元素进行计数,然后返回只有一个元素的RDD构成的DStream
reduce(func)对源DStream中的各个RDD中的元素利用func进行聚合操作,然后返回只有一个元素的RDD构成的新的DStream
countByValue()对于元素类型为K的DStream,返回一个元素为(K,Long)键值对形式的新的DStream,Long对应的值为源DStream中各个RDD的key出现的次数
reduceByKey(func, [numTasks])利用func函数对源DStream中的key进行聚合操作,然后返回新的(K,V)对构成的DStream
join(otherStream, [numTasks])输入为(K,V)、(K,W)类型的DStream,返回一个新的(K,(V,W))类型的DStream
cogroup(otherStream, [numTasks])输入为(K,V)、(K,W)类型的DStream,返回一个新的 (K, Seq[V], Seq[W]) 元组类型的DStream
transform(func)通过RDD-to-RDD函数作用于DStream中的各个RDD,可以是任意的RDD操作,从而返回一个新的RDD
updateStateByKey(func)根据key的之前状态值和key的新值,对key进行更新,返回一个新状态的DStream

特殊的Transformations操作:

  • UpdateStateByKey Operation

    UpdateStateByKey用于记录历史记录,保存上次的状态

  • **Window Operations(**开窗函数)

    滑动窗口转换操作:

    在这里插入图片描述

1.红色的矩形就是一个窗口,窗口框住的是一段时间内的数据流

2.这里面每一个time都是时间单元,在官方的例子中,每隔window length是3 time unit, 而且每隔2个单位时间,窗口会slide一次。

**细节:**基于窗口的操作,需要指定2个参数。

  • window length:窗口长度,表示一段时间内数据的容器
  • slide interval:滑动时间间隔,表示每隔多久计算一次

5.2.Output Operations on DStreams

Output Operations可以将DStream的数据输出到外部的数据库或文件系统。当某个Output Operations被调用时(与RDD的Action相同),spark streaming程序才会开始真正的计算过程。

Output OperationMeaning
print()打印到控制台
saveAsTextFiles(prefix, [suffix])保存流的内容为文本文件,文件名为: prefix-TIME_IN_MS[.suffix]
saveAsObjectFiles(prefix, [suffix])保存流的内容为SequenceFile,文件名为: prefix-TIME_IN_MS[.suffix]
saveAsHadoopFiles(prefix, [suffix])保存流的内容为hadoop文件,文件名为: prefix-TIME_IN_MS[.suffix]
foreachRDD(func)对Dstream里面的每个RDD执行func

6.DStream操作实战

6.1.接收socket数据,实现单词计数WordCount

6.1.1.架构图

在这里插入图片描述

6.1.2.实现流程
6.1.2.1.安装netcat

说明:netcat是一个通过tcp/udp协议,在网络中进行数据读写的工具,被称为“瑞士军刀”。利用netcat可以将网络中一端的数据,完整发送到另外一台主机终端显示或存储。可以用于文件传输、及时通信等。

yum install -y nc

netcat工具简单应用案例:

#扫描端口:
nc -v www.baidu.com 80

#及时通信,将hadoop01作为server主机,通信端口设置为10000
nc -l 10000

#将hadoop02作为client主机,连接到hadoop01的10000端口
 nc 192.168.80.21 10000


图一:hadoop01

在这里插入图片描述

图二:hadoop02

在这里插入图片描述

6.1.2.2.通过netcat向指定端口发送数据
#node01
nc -lk 10000
6.1.2.3.开发spark Streaming案例程序
  • 创建项目

在这里插入图片描述

  • 导入依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>cn.liny</groupId>
        <artifactId>spark-teach-day07-01project</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <packaging>jar</packaging>
    
        <properties>
            <scala.version>2.11.8</scala.version>
            <spark.version>2.0.2</spark.version>
        </properties>
    
        <dependencies>
            <!--scala依赖-->
            <dependency>
                <groupId>org.scala-lang</groupId>
                <artifactId>scala-library</artifactId>
                <version>${scala.version}</version>
            </dependency>
            <!--spark依赖-->
            <dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-streaming_2.11</artifactId>
                <version>${spark.version}</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <!--scala编译插件-->
                <plugin>
                    <groupId>org.scala-tools</groupId>
                    <artifactId>maven-scala-plugin</artifactId>
                    <version>2.15.2</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>compile</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <!-- java 编译插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.2</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
        
    </project>
    
  • 编写案例代码

package cn.liny.stream

import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

/**
  * 学习spark Streaming接收Socket数据,实现单词计数
  */
object WordCountByStreaming {

  def main(args: Array[String]): Unit = {
    // 1.创建SparkConf对象
    val conf: SparkConf = new SparkConf().setAppName("WordCountByStreaming").setMaster("local[2]")

    // 2.创建SparkContext对象
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")

    // 3.创建StreamingContext对象
    /**
      * 参数说明:
      *   参数一:SparkContext对象
      *   参数二:每个批次的间隔时间
      */
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))

    // 4.注册监听Ip地址和端口,接收数据
    /**
      * 参数说明:
      *   参数一:服务器主机的ip地址
      *   参数二:服务端口
      */
    val dataLines: ReceiverInputDStream[String] = ssc.socketTextStream("192.168.53.100",10000)

    // 5.切分每一行记录
    val wordsDS: DStream[String] = dataLines.flatMap(_.split(" "))

    // 6.每个单词计数为1
    val wordAndOneDS: DStream[(String, Int)] = wordsDS.map((_,1))

    // 7.聚合操作
    val resultDS: DStream[(String, Int)] = wordAndOneDS.reduceByKey(_+_)

    // 8.通过Output Operations操作打印数据
    resultDS.print()

    // 9.开启流式计算
    ssc.start()

    // 阻塞一直运行
    ssc.awaitTermination()
  }

}
6.1.3.执行查看效果

图一:

在这里插入图片描述

图二:

在这里插入图片描述

6.1.4.代码步骤小结

1.创建SparkConf对象

– 细节:在spark Streaming中,执行任务至少需要2core的cpu内核资源。分别用于接收数据,和处理数据。

2.创建SparkContext对象

3.创建StreamingContext对象。需要提供两个参数(参数一:SparkContext对象,参数二:每个批次的间隔时间)

4.注册监听Ip地址和端口,接收数据。需要提供两个参数(参数一:服务器主机的ip地址,参数二:服务端口)

5.根据业务需求,将数据进行Transformations 操作

6.根据业务需求,将处理好的结果数据进行Output Operations操作

7.开启流式计算,并且要一直阻塞运行

6.2.接收socket数据,实现所有批次单词计数结果累加

在第一个案例中存在一个问题:每个批次的单词次数都被正确的统计出来,但是结果不能累加!

如果将所有批次的结果数据进行累加,使用**updateStateByKey(func)**来实现。

6.2.1.编写案例代码
package cn.liny.stream

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}

/**
  * 学习spark Streaming接收Socket数据,实现单词计数(将所有批次结果进行累加)
  */
object WordCountByStreamingSum {
  def main(args: Array[String]): Unit = {
    // 1.创建SparkConf对象
    val conf: SparkConf = new SparkConf().setAppName("WordCountByStreaming").setMaster("local[2]")

    // 2.创建SparkContext对象
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")

    // 3.创建StreamingContext对象
    /**
      * 参数说明:
      *   参数一:SparkContext对象
      *   参数二:每个批次的间隔时间
      */
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))
    /**
      * 细节:实现累加,需要设置checkpoint目录
      */
    ssc.checkpoint("./wordsck")

    // 4.注册监听Ip地址和端口,接收数据
    /**
      * 参数说明:
      *   参数一:服务器主机的ip地址
      *   参数二:服务端口
      */
    val dataLines: ReceiverInputDStream[String] = ssc.socketTextStream("192.168.53.100",10000)

    // 5.切分每一行记录
    val wordsDS: DStream[String] = dataLines.flatMap(_.split(" "))

    // 6.每个单词计数为1
    val wordAndOneDS: DStream[(String, Int)] = wordsDS.map((_,1))

    // 7.实现所有批次结果数据累加
    /**
      * 实现方法:updateStateByKey
      * 参数:myFunc
      */
    val resultDS: DStream[(String, Int)] = wordAndOneDS.updateStateByKey(myFunc)

    // 8.通过Output Operations操作打印数据
    resultDS.print()

    // 9.开启流式计算
    ssc.start()

    // 阻塞一直运行
    ssc.awaitTermination()
  }

  /**
    * 定义实现批次结果数据累加函数
    * 参数说明:
    *   currentValues:当前批次每个单词出现的所有1。比如:(spark,1)(spark,1)
    *   historyValues:之前所有批次中每个单词出现的总次数。比如:(spark,50)
    *
    *  返回值:
    *   当前批次,与之前所有批次,每个单词出现的总次数(累加结果)
    */
  def myFunc(currentValues:Seq[Int],historyValues:Option[Int]):Option[Int]={

    // 将当前批次每个单词出现的所有1求和
    val currentSum: Int = currentValues.sum
    // 将当前批次单词求和结果,与之前批次总结果进行累加
    val resultSum: Int = currentSum+historyValues.getOrElse(0)

    Some(resultSum)
  }

}
6.2.2.测试

图一:

在这里插入图片描述

图二:

在这里插入图片描述

6.2.3.小结

1.设置checkpoint目录:

// 3.创建StreamingContext对象
/**
  * 参数说明:
  *   参数一:SparkContext对象
  *   参数二:每个批次的间隔时间
  */
val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))
/**
  * 细节:实现累加,需要设置checkpoint目录
  */
ssc.checkpoint("./wordsck")

2.通过updateStateByKey实现批次结果累加:

// 7.实现所有批次结果数据累加
/**
  * 实现方法:updateStateByKey
  * 参数:myFunc
  */
val resultDS: DStream[(String, Int)] = wordAndOneDS.updateStateByKey(myFunc)

3.updateStateByKey方法中函数定义:

/**
  * 定义实现批次结果数据累加函数
  * 参数说明:
  *   currentValues:当前批次每个单词出现的所有1。比如:(spark,1)(spark,1)
  *   historyValues:之前所有批次中每个单词出现的总次数。比如:(spark,50)
  *
  *  返回值:
  *   当前批次,与之前所有批次,每个单词出现的总次数(累加结果)
  */
def myFunc(currentValues:Seq[Int],historyValues:Option[Int]):Option[Int]={

  // 将当前批次每个单词出现的所有1求和
  val currentSum: Int = currentValues.sum
  // 将当前批次单词求和结果,与之前批次总结果进行累加
  val resultSum: Int = currentSum+historyValues.getOrElse(0)

  Some(resultSum)
}

6.3.开窗函数reduceByKeyAndWindow,实现单词计数

说明:开窗函数可以用于计算一段时间内的数据。

6.3.1.编写案例代码
package cn.liny.stream

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}

/**
  * 学习spark Streaming接收Socket数据,开窗函数:统计一定时间内单词出现的次数
  */
object WordCountByStreamingWin {

  def main(args: Array[String]): Unit = {
    // 1.创建SparkConf对象
    val conf: SparkConf = new SparkConf().setAppName("WordCountByStreaming").setMaster("local[2]")

    // 2.创建SparkContext对象
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")

    // 3.创建StreamingContext对象
    /**
      * 参数说明:
      *   参数一:SparkContext对象
      *   参数二:每个批次的间隔时间
      */
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))
    // 设置checkpoint目录
    ssc.checkpoint("./winck")

    // 4.注册监听Ip地址和端口,接收数据
    /**
      * 参数说明:
      *   参数一:服务器主机的ip地址
      *   参数二:服务端口
      */
    val dataLines: ReceiverInputDStream[String] = ssc.socketTextStream("192.168.53.100",10000)

    // 5.切分每一行记录
    val wordsDS: DStream[String] = dataLines.flatMap(_.split(" "))

    // 6.每个单词计数为1
    val wordAndOneDS: DStream[(String, Int)] = wordsDS.map((_,1))

    // 7.开窗函数(reduceByKeyAndWindow):统计一段时间内单词出现的次数
    /**
      * 开窗函数参数说明:
      *    reduceFunc:业务操作函数
      *   windowDuration:窗口长度,表示window框住的时间长度。如本例5秒切分一次RDD,框10秒,就会保留最近2次切分的RDD
      *   slideDuration:窗口滑动时间间隔,表示window滑动的时间长度,即每隔多久执行计算
      */
    val resultDS: DStream[(String, Int)] = wordAndOneDS.reduceByKeyAndWindow(
                                          (x:Int,y:Int)=>x+y,
                                           Seconds(10),
                                           Seconds(10)
                                        )

    // 8.通过Output Operations操作打印数据
    resultDS.print()

    // 9.开启流式计算
    ssc.start()

    // 阻塞一直运行
    ssc.awaitTermination()
  }

}
6.3.2.小结

1.开窗函数reduceByKeyAndWindow说明:

// 7.开窗函数(reduceByKeyAndWindow):统计一段时间内单词出现的次数
/**
  * 开窗函数参数说明:
  *    reduceFunc:业务操作函数
  *   windowDuration:窗口长度,表示window框住的时间长度。如本例5秒切分一次RDD,框10秒,就会保留最近2次切分的RDD
  *   slideDuration:窗口滑动时间间隔,表示window滑动的时间长度,即每隔多久执行计算
  */
val resultDS: DStream[(String, Int)] = wordAndOneDS.reduceByKeyAndWindow(
                                      (x:Int,y:Int)=>x+y,
                                       Seconds(10),
                                       Seconds(10)
                                    )

2.窗口长度windowDuration,如果大于窗口滑动时间slideDuration,会发生重复计算

3.窗口长度windowDuration,如果小于窗口滑动时间slideDuration,会丢失数据

4.在实际应用中,窗口长度和窗口滑动时间,都指定为批次的间隔时间的整数倍

在这里插入图片描述

6.4.开窗函数统计一定时间内的热门词汇

6.4.1.需求

sparkStreaming每隔5s计算一次当前在窗口大小为10s内的数据,然后将单词出现次数最多的前3位进行输出打印。

6.4.2.编写案例代码
package cn.liny.stream

import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

/**
  * 学习spark Streaming接收Socket数据,开窗函数:
  * 每隔5s计算一次当前在窗口大小为10s内的数据,然后将单词出现次数最多的前3位进行输出打印
  */
object WordCountByStreamingWinHot {

  def main(args: Array[String]): Unit = {
    // 1.创建SparkConf对象
    val conf: SparkConf = new SparkConf().setAppName("WordCountByStreamingWinHot").setMaster("local[2]")

    // 2.创建SparkContext对象
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")

    // 3.创建StreamingContext对象
    /**
      * 参数说明:
      *   参数一:SparkContext对象
      *   参数二:每个批次的间隔时间
      */
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))
    // 设置checkpoint目录
    ssc.checkpoint("./winck")

    // 4.注册监听Ip地址和端口,接收数据
    /**
      * 参数说明:
      *   参数一:服务器主机的ip地址
      *   参数二:服务端口
      */
    val dataLines: ReceiverInputDStream[String] = ssc.socketTextStream("192.168.53.100",10000)

    // 5.切分每一行记录
    val wordsDS: DStream[String] = dataLines.flatMap(_.split(" "))

    // 6.每个单词计数为1
    val wordAndOneDS: DStream[(String, Int)] = wordsDS.map((_,1))

    // 7.开窗函数(reduceByKeyAndWindow):统计一段时间内单词出现的次数
    /**
      * 开窗函数参数说明:
      *    reduceFunc:业务操作函数
      *   windowDuration:窗口长度,表示window框住的时间长度。如本例5秒切分一次RDD,框10秒,就会保留最近2次切分的RDD
      *   slideDuration:窗口滑动时间间隔,表示window滑动的时间长度,即每隔多久执行计算
      */
    val resultDS: DStream[(String, Int)] = wordAndOneDS.reduceByKeyAndWindow(
                                          (x:Int,y:Int)=>x+y,
                                           Seconds(10),
                                           Seconds(5)
                                        )

    // 8.降序处理后,取前3位
    val sortResultRDD: DStream[(String, Int)] = resultDS.transform(rdd => {
      // 降序处理
      val sortRDD: RDD[(String, Int)] = rdd.sortBy(_._2, false)

      // 取前3位
      val top3RDD: Array[(String, Int)] = sortRDD.take(3)

      println("--------------print top 3 begin--------------")
      top3RDD.foreach(println)
      println("--------------print top 3 end--------------")

      sortRDD
    })

    // 打印排序后的数据
    sortResultRDD.print()

    // 9.开启流式计算
    ssc.start()

    // 阻塞一直运行
    ssc.awaitTermination()
  }

}

7.spark Streaming整合flume实战

7.1.搭建flume环境

7.1.1.flume介绍

flume是Cloudera提供的一个高可用高可靠分布式的海量日志采集、聚合和传输的系统。flume支持在日志系统中定制各类数据发送方,用于收集数据 。同时,flume提供对数据进行简单处理,并写到各种数据接收方(可定制)的能力。

flume的三个组件:

在这里插入图片描述

Source:从数据发送器接收数据 ,并将接收的数据以flume的event格式传递给一个或者多个通道channel 。

Channel:channel是一种短暂的存储容器 ,它将从source处接收到的event格式的数据缓存起来 ,直到它们被sink消费。它在source和sink间起到桥梁的作用 。

Sink:sink将数据存储到集中存储器 。它从channal消费数据(events)并将其传递给目标地。 目标地可能是另一个sink,也可能HDFS或者HBase 等存储系统。

7.1.2.安装flume
7.1.2.1.下载flume

在这里插入图片描述

7.1.2.2.上传flume安装包到指定节点,并解压cd

在这里插入图片描述

7.1.2.3.修改配置文件
#node01
cd /export/servers/apache-flume-1.6.0-bin/conf
mv flume-env.sh.template flume-env.sh
vi flume-env.sh

#配置jdk环境变量
export JAVA_HOME=/export/servers/jdk1.8.0_141
7.1.2.4.编写agent配置文件
vi netcat-logger.conf

#定义agent 中各组件的名字
a1.sources = r1
a1.sinks = k1
a1.channels = c1

# 描述和配置 source 组件:r1
a1.sources.r1.type = netcat
a1.sources.r1.bind = 192.168.53.100
a1.sources.r1.port = 44444

# 描述和配置 sink 组件:k1
a1.sinks.k1.type = logger

# 描述和配置 channel 组件,此处使用是内存缓存的方式
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100

# 描述和配置 source channel sink 之间的连接关系
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
7.1.2.5.初始体验
#启动agent(node01)
cd /export/servers/apache-flume-1.6.0-bin/bin

./flume-ng agent --conf conf --conf-file ../conf/netcat-logger.conf --name a1 -Dflume.root.logger=INFO,console
#node02
telnet 192.168.53.100 44444

在这里插入图片描述

7.1.3.flume整合spark Streaming
7.1.3.1.下载整合依赖包

在这里插入图片描述

7.1.3.2.将整合依赖包,上传到flume/lib目录
ll /export/servers/apache-flume-1.6.0-bin/lib

在这里插入图片描述

7.1.3.3.替换flume的scala依赖包

1.删除flume中原有的scala包

rm -rf scala-library-2.10.1.jar

2.从spark安装目录中拷贝scala安装包

cp /export/servers/spark-2.0.2-bin-hadoop2.7/jars/scala-library-2.11.8.jar /export/servers/apache-flume-1.6.0-bin/lib/

在这里插入图片描述

7.1.3.4.编写flume的agent配置文件
cd /export/servers/apache-flume-1.6.0-bin/conf
vi flume-poll-spark.conf

#定义agent中各组件的名字
a1.sources = r1
a1.sinks = k1
a1.channels = c1

#描述和配置source组件:r1
a1.sources.r1.channels = c1
a1.sources.r1.type = spooldir
a1.sources.r1.spoolDir =/export/servers/sparkflume
a1.sources.r1.fileHeader = true

#描述和配置channel组件,此处使用是内存缓存的方式
a1.channels.c1.type =memory
a1.channels.c1.capacity = 20000
a1.channels.c1.transactionCapacity=5000


#描述和配置sink组件:k1
a1.sinks.k1.channel = c1
a1.sinks.k1.type = org.apache.spark.streaming.flume.sink.SparkSink
a1.sinks.k1.hostname=node01
a1.sinks.k1.port = 8888
a1.sinks.k1.batchSize= 2000

在这里插入图片描述

7.1.3.5准备数据文件
cd /export/servers/sparkflume
vi words.txt

hello me me
hello you
hello her
hello you you

在这里插入图片描述

7.1.3.6.启动flume的agent
#启动flume agent

cd /export/servers/apache-flume-1.6.0-bin

bin/flume-ng agent -n a1 -c conf/ -f conf/flume-poll-spark.conf -Dflume.root.logger=INFO,console

7.2.poll方式

7.2.1.导入依赖
<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming-flume_2.11</artifactId>
    <version>${spark.version}</version>
</dependency>
7.2.2.编写案例代码
package cn.ly.flume

import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.flume.{FlumeUtils, SparkFlumeEvent}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

/**
  * 学习spark Streaming从flume拉取数据,实现单词计数
  */
object WordCountByStreamingPollFlume {
  def main(args: Array[String]): Unit = {
    // 1.创建SparkConf对象
    val conf: SparkConf = new SparkConf().setAppName("WordCountByStreamingPollFlume").setMaster("local[2]")

    // 2.创建SparkContext对象
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")

    // 3.创建StreamingContext对象
    /**
      * 参数说明:
      *   参数一:SparkContext对象
      *   参数二:每个批次的间隔时间
      */
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))
    /**
      * 细节:实现累加,需要设置checkpoint目录
      */
    ssc.checkpoint("./flumeck")

    // 4.通过FlumeUtils调用createPollingStream方法获取flume中的数据
    /**
      * 参数说明:
      *   参数一:StreamingContext对象
      *   参数二:flume的agent主机名称
      *   参数三:flume的agent主机端口
      */
      val flumeDS: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createPollingStream(ssc,"192.168.53.100",8888)

    // 5.获取flume中event的body {"headers":xxxxxx,"body":xxxxx}
    val dataLines: DStream[String] = flumeDS.map(x =>new String(x.event.getBody.array()))

    // 6.切分每一行记录
    val wordsDS: DStream[String] = dataLines.flatMap(_.split(" "))

    // 7.每个单词计数为1
    val wordAndOneDS: DStream[(String, Int)] = wordsDS.map((_,1))

    // 8.实现所有批次结果数据累加
    /**
      * 实现方法:updateStateByKey
      * 参数:myFunc
      */
    val resultDS: DStream[(String, Int)] = wordAndOneDS.updateStateByKey(myFunc)

    // 9.通过Output Operations操作打印数据
    resultDS.print()

    // 10.开启流式计算
    ssc.start()

    // 阻塞一直运行
    ssc.awaitTermination()
  }

  /**
    * 定义实现批次结果数据累加函数
    * 参数说明:
    *   currentValues:当前批次每个单词出现的所有1。比如:(spark,1)(spark,1)
    *   historyValues:之前所有批次中每个单词出现的总次数。比如:(spark,50)
    *
    *  返回值:
    *   当前批次,与之前所有批次,每个单词出现的总次数(累加结果)
    */
  def myFunc(currentValues:Seq[Int],historyValues:Option[Int]):Option[Int]={

    // 将当前批次每个单词出现的所有1求和
    val currentSum: Int = currentValues.sum
    // 将当前批次单词求和结果,与之前批次总结果进行累加
    val resultSum: Int = currentSum+historyValues.getOrElse(0)

    Some(resultSum)
  }

}
7.2.3.测试

图一:flume主机端

在这里插入图片描述

图二:spark Streaming程序端

在这里插入图片描述

7.2.4.关键步骤小结

1.导入依赖

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming-flume_2.11</artifactId>
    <version>${spark.version}</version>
</dependency>

2.通过FlumeUtils调用方法,拉取flume数据

// 4.通过FlumeUtils调用createPollingStream方法获取flume中的数据
    /**
      * 参数说明:
      *   参数一:StreamingContext对象
      *   参数二:flume的agent主机名称
      *   参数三:flume的agent主机端口
      */
      val flumeDS: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createPollingStream(ssc,"192.168.80.21",8888)

3.将拉取到的flume数据,获取body部分,并且转换成字符串内容

// 5.获取flume中event的body {"headers":xxxxxx,"body":xxxxx}
    val dataLines: DStream[String] = flumeDS.map(x =>new String(x.event.getBody.array()))

7.3.push方式

7.3.1.编写flume的agent配置文件
cd /export/servers/apache-flume-1.6.0-bin/conf
vi flume-push-spark.conf

#定义agent中各组件的名字
a1.sources = r1
a1.sinks = k1
a1.channels = c1

#描述和配置source组件:r1
a1.sources.r1.channels = c1
a1.sources.r1.type = spooldir
a1.sources.r1.spoolDir =/export/servers/sparkflume
a1.sources.r1.fileHeader = true


#描述和配置channel组件,此处使用是内存缓存的方式
a1.channels.c1.type =memory
a1.channels.c1.capacity = 20000
a1.channels.c1.transactionCapacity=5000


#描述和配置sink组件:k1
a1.sinks.k1.channel = c1
a1.sinks.k1.type = avro
a1.sinks.k1.hostname=192.168.43.142
a1.sinks.k1.port = 9999
a1.sinks.k1.batchSize= 2000

**细节:**指定的是spark Streaming程序所在的主机Ip地址和端口(非虚拟机)

a1.sinks.k1.hostname=192.168.80.1

vi flume-push-spark.conf

7.3.2.编写案例代码
package cn.ly.flume

import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.flume.{FlumeUtils, SparkFlumeEvent}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

/**
  * 学习spark Streaming从flume推送过来的数据,实现单词计数
  */
object WordCountByStreamingPushFlume {
  def main(args: Array[String]): Unit = {
    // 1.创建SparkConf对象
    val conf: SparkConf = new SparkConf().setAppName("WordCountByStreamingPollFlume").setMaster("local[2]")

    // 2.创建SparkContext对象
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")

    // 3.创建StreamingContext对象
    /**
      * 参数说明:
      *   参数一:SparkContext对象
      *   参数二:每个批次的间隔时间
      */
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))
    /**
      * 细节:实现累加,需要设置checkpoint目录
      */
    ssc.checkpoint("./flumeck")

    // 4.通过FlumeUtils调用createStream方法获取flume中的数据
    /**
      * 参数说明:
      *   参数一:StreamingContext对象
      *   参数二:flume的agent主机名称
      *   参数三:flume的agent主机端口
      */
      val flumeDS: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createStream(ssc,"192.168.43.142",9999)

    // 5.获取flume中event的body {"headers":xxxxxx,"body":xxxxx}
    val dataLines: DStream[String] = flumeDS.map(x =>new String(x.event.getBody.array()))

    // 6.切分每一行记录
    val wordsDS: DStream[String] = dataLines.flatMap(_.split(" "))

    // 7.每个单词计数为1
    val wordAndOneDS: DStream[(String, Int)] = wordsDS.map((_,1))

    // 8.实现所有批次结果数据累加
    /**
      * 实现方法:updateStateByKey
      * 参数:myFunc
      */
    val resultDS: DStream[(String, Int)] = wordAndOneDS.updateStateByKey(myFunc)

    // 9.通过Output Operations操作打印数据
    resultDS.print()

    // 10.开启流式计算
    ssc.start()

    // 阻塞一直运行
    ssc.awaitTermination()
  }

  /**
    * 定义实现批次结果数据累加函数
    * 参数说明:
    *   currentValues:当前批次每个单词出现的所有1。比如:(spark,1)(spark,1)
    *   historyValues:之前所有批次中每个单词出现的总次数。比如:(spark,50)
    *
    *  返回值:
    *   当前批次,与之前所有批次,每个单词出现的总次数(累加结果)
    */
  def myFunc(currentValues:Seq[Int],historyValues:Option[Int]):Option[Int]={

    // 将当前批次每个单词出现的所有1求和
    val currentSum: Int = currentValues.sum
    // 将当前批次单词求和结果,与之前批次总结果进行累加
    val resultSum: Int = currentSum+historyValues.getOrElse(0)

    Some(resultSum)
  }

}
7.3.3.启动spark Streaming案例程序

在这里插入图片描述

7.3.4.启动flume的agent
cd /export/servers/apache-flume-1.6.0-bin

bin/flume-ng agent -n a1 -c conf/ -f conf/flume-push-spark.conf -Dflume.root.logger=INFO,console
7.3.5.关键步骤小结

1.通过FlumeUtils的createStream方法,接收flume推送的数据

// 4.通过FlumeUtils调用createStream方法获取flume中的数据
    /**
      * 参数说明:
      *   参数一:StreamingContext对象
      *   参数二:flume的agent主机名称
      *   参数三:flume的agent主机端口
      */
      val flumeDS: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createStream(ssc,"192.168.43.142",9999)

2.获取flume中event的body {“headers”:xxxxxx,“body”:xxxxx}

 // 5.获取flume中event的body {"headers":xxxxxx,"body":xxxxx}
    val dataLines: DStream[String] = flumeDS.map(x =>new String(x.event.getBody.array()))

3.优先启动spark Streaming应用

4.启动flume的agent

bin/flume-ng agent -n a1 -c conf/ -f conf/flume-push-spark.conf -Dflume.root.logger=INFO,console

5.在企业项目中,推荐使用从flume拉取数据的方式。推送数据可能会丢失数据

8.spark Streaming整合kafka实战

8.1.搭建kafka集群环境

8.1.1.kafka介绍

Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统

相关术语介绍:

术语说明
BrokerKafka集群包含一个或多个服务器,服务器即broker
Topic每条发布到Kafka集群的消息都有一个类别,类别即Topic
PartitionPartition是物理上的概念,每个Topic包含一个或多个Partition
Producer消息生产者,负责发布消息到Kafka broker
Consumer消息消费者,向Kafka broker读取消息的客户端
Consumer Group每个Consumer属于一个特定的Consumer Group 。 如果没有指定group name,则属于默认的group

kafka架构图:

在这里插入图片描述

8.1.2.集群环境搭建
8.1.2.1.下载kafka

官网地址:http://kafka.apache.org/downloads

在这里插入图片描述

8.1.2.2.上传到linux主机节点,并解压
#解压kafka
cd /export/servers

tar -zxvf kafka_2.11-0.10.2.1.tgz

#删除压缩包,并且重新命名kafka文件目录

rm -rf kafka_2.11-0.10.2.1.tgz

mv kafka_2.11-0.10.2.1/ kafka211010

在这里插入图片描述

图二:

在这里插入图片描述

8.1.2.3.配置kafka配置文件
#node01
cd /export/servers/kafka211010/config

vi server.properties
8.1.2.4.将配置好的kafka远程拷贝到其他节点
scp -r kafka211010/ root@node02:/export/servers/kafka211010

scp -r kafka211010/ root@node03:/export/servers/kafka211010

8.1.2.5.修改其他节点的server.properties配置
cd /export/servers/kafka211010/config

vim server.properties

#node02节点

broker.id=1
port=9092
host.name=hadoop02

#node03节点:
broker.id=2
port=9092
host.name=hadoop03


8.1.2.6.启动zookeeper集群

在这里插入图片描述

8.1.2.7.启动kafka服务器

集群节点:node01,node02,node03

#分别在三台节点执行
 ##启动kafka集群
/export/servers/kafka211010/bin/kafka-server-start.sh -daemon /export/servers/kafka211010/config/server.properties

 ## 停止kafka集群
/export/servers/kafka211010/bin/kafka-server-stop.sh
8.1.2.8.集群环境测试
  • 查看topic list

    #查看topic 列表
    /export/servers/kafka211010/bin/kafka-topics.sh --list --zookeeper node01:2181,node02:2181,node03:2181
    
    #查看具体某一个topic消息
    /export/servers/kafka211010/bin/kafka-topics.sh  --describe --zookeeper node01:2181,node02:2181,node03:2181 --topic topic1
    
    #创建topic
    # --create:表示创建
    # --zookeeper 后面的参数是zk的集群节点
    # --replication-factor 3 :表示复本数
    # --partitions 3:表示分区数
    # --topic topic4:表示topic的主题名称
    
    /export/servers/kafka211010/bin/kafka-topics.sh --create --zookeeper node01:2181,node02:2181,node03:2181 --replication-factor 3 --partitions 3 --topic topic4
    
    
    #删除topic
    /export/servers/kafka211010/bin/kafka-topics.sh --delete --zookeeper node01:2181,node02:2181,node03:2181 --topic topic4
    
    
  • 创建生产者

    #创建生产者,生产消息
    /export/servers/kafka211010/bin/kafka-console-producer.sh --broker-list node01:9092,node02:9092,node03:9092 --topic topic5
    

    在这里插入图片描述

  • 创建消费者

    #创建消费者,消费消息
    /export/servers/kafka211010/bin/kafka-console-consumer.sh --zookeeper node01:2181,node02:2181,node03:2181 --topic topic5 --consumer-property group.id=my-consumer-g  --delete-consumer-offsets --from-beginning 0
    

    在这里插入图片描述

8.2.createDstream方式整合kafka

8.2.1.导入依赖
<dependency>
     <groupId>org.apache.spark</groupId>
     <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
     <version>${spark.version}</version>
  </dependency>
8.2.2.启动zookeeper集群

在这里插入图片描述

8.2.3.启动kafka集群
/usr/local/develop/kafka211010/bin/kafka-server-start.sh -daemon /usr/local/develop/kafka211010/config/server.properties
8.2.4.创建topic
/export/servers/kafka211010/bin/kafka-topics.sh --create --zookeeper node01:2181 --replication-factor 1 --partitions 3 --topic kafka_spark
8.2.5.通过生产者,向topic发送消息
/export/servers/kafka211010/bin/kafka-console-producer.sh --broker-list node01:9092 --topic  kafka_spark
8.2.6.编写spark Streaming案例程序
package cn.liny.kafka

import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.immutable

/**
  * 学习sparkStreaming对接kafka实现单词计数----采用receiver(高级API)
  */
object SparkStreamingKafka_Receiver {

  def main(args: Array[String]): Unit = {
    // 1.创建SparkConf对象
    val conf: SparkConf = new SparkConf()
              .setAppName("SparkStreamingKafka_Receiver")
              .setMaster("local[4]")
              .set("spark.streaming.receiver.writeAheadLog.enable","true")// 开启wal预写日志,保存数据源的可靠性

    // 2.创建SparkContext对象
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")

    // 3.创建StreamingContext对象
    /**
      * 参数说明:
      *   参数一:SparkContext对象
      *   参数二:每个批次的间隔时间
      */
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))
    //设置checkpoint目录
    ssc.checkpoint("./Kafka_Receiver")

    // 4.通过KafkaUtils.createStream对接kafka
    // 4.1.定义zk地址
    val zkQuorum="node01:2181,node02:2181,node03:2181"
    // 4.2.定义消费者组
    val groupId="spark_receiver1"
    // 4.3.定义topic相关信息 Map[String, Int]
    val topics=Map("kafka_spark" -> 2)// 这里的value表示topic中每一个分区被N个线程消费

    // 4.4.同时开启3个receiver接收数据
    val receiverDstream: immutable.IndexedSeq[ReceiverInputDStream[(String, String)]] = (1 to 3).map(x => {
      val stream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics)
      stream
    }
    )

    // 4.5.使用ssc.union方法合并所有的receiver中的数据
    val unionDStream: DStream[(String, String)] = ssc.union(receiverDstream)


    // 5.获取topic中的数据
    val topicData: DStream[String] = unionDStream.map(_._2)

    // 6.切分每一行,每个单词计为1
    val wordAndOne: DStream[(String, Int)] = topicData.flatMap(_.split(" ")).map((_,1))

    // 7.相同单词出现的次数累加
    val resultDS: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_)

    // 8.通过Output Operations操作打印数据
    resultDS.print()

    // 9.开启流式计算
    ssc.start()

    // 阻塞一直运行
    ssc.awaitTermination()
  }

}
8.2.7.关键步骤小结

1.创建SparkConf对象,设置的线程数要大于receiver个数。同时要开启wal预写日志,保证数据的不丢失。

 // 1.创建SparkConf对象
    val conf: SparkConf = new SparkConf()
              .setAppName("SparkStreamingKafka_Receiver")
              .setMaster("local[4]")
              .set("spark.streaming.receiver.writeAheadLog.enable","true")// 开启wal预写日志,保存数据源的可靠性

2.通过KafkaUtils.createStream对接kafka

 // 4.通过KafkaUtils.createStream对接kafka
    // 4.1.定义zk地址
    val zkQuorum="hadoop01:2181,hadoop02:2181,hadoop03:2181"
    // 4.2.定义消费者组
    val groupId="spark_receiver1"
    // 4.3.定义topic相关信息 Map[String, Int]
    val topics=Map("kafka_spark" -> 2)// 这里的value表示topic中每一个分区被N个线程消费

    // 4.4.同时开启3个receiver接收数据
    val receiverDstream: immutable.IndexedSeq[ReceiverInputDStream[(String, String)]] = (1 to 3).map(x => {
      val stream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics)
      stream
    }
    )

    // 4.5.使用ssc.union方法合并所有的receiver中的数据
    val unionDStream: DStream[(String, String)] = ssc.union(receiverDstream)

3.获取topic中的数据

// 5.获取topic中的数据
    val topicData: DStream[String] = unionDStream.map(_._2)

4.基于receiver的方式:是使用Kafka的高级API,topic的offset偏移量在ZooKeeper中。这种方式配合WAL预写机制,可以保证数据零丢失,但是可能会出现相同的数据,被处理两次。

8.3.createDirectStream方式整合kafka

8.3.1.编写案例代码
package cn.liny.kafka

import kafka.serializer.StringDecoder
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

/**
  * 学习sparkStreaming对接kafka实现单词计数----采用Direct(低级API)
  */
object SparkStreamingKafka_Direct {
  def main(args: Array[String]): Unit = {
    // 1.创建SparkConf对象
    val conf: SparkConf = new SparkConf()
      .setAppName("SparkStreamingKafka_Direct")
      .setMaster("local[2]")

    // 2.创建SparkContext对象
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")

    // 3.创建StreamingContext对象
    /**
      * 参数说明:
      *   参数一:SparkContext对象
      *   参数二:每个批次的间隔时间
      */
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))
    //设置checkpoint目录
    ssc.checkpoint("./Kafka_Direct")

    // 4.通过KafkaUtils.createDirectStream对接kafka(采用是kafka低级api偏移量不受zk管理)
    // 4.1.配置kafka相关参数
    val kafkaParams=Map("metadata.broker.list"->"192.168.53.100:9092,192.168.53.110:9092,192.168.53.120:9092","group.id"->"kafka_Direct")
    // 4.2.定义topic
    val topics=Set("kafka_spark")

    val dstream: InputDStream[(String, String)] = KafkaUtils
      .createDirectStream[String,String,StringDecoder,StringDecoder](ssc,kafkaParams,topics)

    // 5.获取topic中的数据
    val topicData: DStream[String] = dstream.map(_._2)

    // 6.切分每一行,每个单词计为1
    val wordAndOne: DStream[(String, Int)] = topicData.flatMap(_.split(" ")).map((_,1))

    // 7.相同单词出现的次数累加
    val resultDS: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_)

    // 8.通过Output Operations操作打印数据
    resultDS.print()

    // 9.开启流式计算
    ssc.start()

    // 阻塞一直运行
    ssc.awaitTermination()
  }
}
8.3.2.关键步骤小结

1.通过KafkaUtils .createDirectStream对接kafka

// 4.通过KafkaUtils.createDirectStream对接kafka(采用是kafka低级api偏移量不受zk管理)
// 4.1.配置kafka相关参数
val kafkaParams=Map("metadata.broker.list"->"192.168.80.21:9092,192.168.80.22:9092,192.168.80.23:9092","group.id"->"kafka_Direct")
// 4.2.定义topic
val topics=Set("kafka_spark")

val dstream: InputDStream[(String, String)] = KafkaUtils
  .createDirectStream[String,String,StringDecoder,StringDecoder](ssc,kafkaParams,topics)

2.Direct方式的优点:

  • 简化并行

    不需要创建多个kafka输入流,然后union它们。sparkStreaming将会创建和kafka分区数相同的rdd的分区数,并行读取数据。

  • 高效

    receiver实现方式,数据的零丢失是将数据预先保存在WAL中,会导致数据要拷贝两次,效率低下。Direct方式不需要拷贝两次数据,直接获取kafka的topic数据即可处理。

  • 恰好一次语义(Exactly-once-semantics)

    Receiver读取kafka数据是通过kafka高层次api,把偏移量写入zookeeper集群中。可能会因为sparkStreaming和zookeeper中保存的偏移量不一致而导致数据被消费了多次。

    Exactly-once-semantics通过实现kafka低层次api,偏移量仅仅被ssc保存在checkpoint目录中,消除了zk和ssc偏移量不一致的问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值