Flume+Spark Streaming

1 概述

Apache Flume是一种分布式,可靠且可用的服务,用于高效地收集,汇总和移动大量日志数据。 这里我们学习如何配置Flume和Spark Streaming来接收来自Flume的数据。 提供两种方法来解决这问题。

注意:从Spark 2.3.0开始,不推荐使用Flume支持。个人也不推荐这种架构,数据量小的情况下可能没什么问题,但是再数据量过大的情况下Streaming流式处理是处理不过来的,必定会造成消息堵塞,所以建议加一个消息中间件例如Kafka。

2 Push-based

方法1:流水线式推送方式
Flume接收的数据直接交给Spark Streaming处理。 所以这种方法中,Spark Streaming需要一个接收器receiver,作为Flume的Avro代理,Flume可以将数据推送到该接收器。 所以这里要注意local的使用。

启动要求:先启动Spark Streaming进行接收数据再启动Flume.
选型结构:netcat => memory ==> avro sink ==> streaming

  • 编写代码
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}

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

    //传入的参数hostname,port
    val Array(hostname,port)=args
      val sparkConf=new SparkConf().setAppName("FlumePush").setMaster("local[2]")
      val ssc=new StreamingContext(sparkConf,Seconds(10))

    // import FlumeUtils
    import org.apache.spark.streaming.flume._
    //create input DStream as follows.
    val flumeStream=FlumeUtils.createStream(ssc,hostname,port.toInt)

    //词频统计
    flumeStream.map(x=>new String(x.event.getBody.array()).trim)
      .flatMap(x=>x.split(",")).map((_,1)).reduceByKey(_+_).print()

    ssc.start()
    ssc.awaitTermination()
  }

}

上面我们是通过外面进行传递hostname和port,所以要进行下面的配置
Flume+Spark Streaming_第1张图片
在Programe arguments传递参数。作者使用的时windows本地运行出现各种错误,选择打包上传就不需要在本地配置参数的,直接在spark-submit的时候传递参数。

  • 配置文件
a1.sources = r1
a1.sinks = k1
a1.channels = c1

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

# Describe the sink
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = localhost
a1.sinks.k1.port = 41414

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

# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1

启动命令

./spark-submit --master local[2] \
--class cn.zhangyu.FlumePush \
--name FlumePush \
/home/hadoop/lib/spark_streaming-1.0-SNAPSHOT.jar \
localhost 41414

这时候提交应用程序的时候肯定会报错,因为我们本地并没有FlumeUtils这个jar包,所以要指定--packages g:a:v参数(有网络才可以用,没有网络可以实现下载好通过–jars参数指定)

./spark-submit --master local[2] \
--class cn.zhangyu.FlumePush \
--packages org.apache.spark:spark-streaming-flume_2.11:2.2.0 \
--name FlumePush \
/home/hadoop/lib/spark_streaming-1.0-SNAPSHOT.jar \
localhost 41414
./flume-ng agent \
--name a1 \
--conf $FLUME_HOME/conf \
--conf-file $FLUME_HOME/conf/flume-sparkStreaming-push \
-Dflume.root.logger=INFO,console

输入数据:

[hadoop@hadoop conf]$ telnet localhost 44444
Trying ::1...
Connected to localhost.
Escape character is '^]'.
a,a,a,b,b,b,c,c,c,d,d,s
OK
a,a,a,b,b,b,c,c,c,d,d,s
OK

结果:

(d,2)
(b,3)
(s,1)
(a,3)
(c,3)

(d,2)
(b,3)
(s,1)
(a,3)
(c,3)

3 Pull-based

使用该种方式Flume不是直接将数据推送到Spark Streaming,而是运行一个自定义的Flume接收器,它允许执行以下操作。

  1. Flume将数据推入接收器,并且数据保持缓冲。
  2. Spark Streaming使用可靠的Flume接收器和并从接收器中提取数据。该事务只有在Spark Streaming接收和复制数据后才会成功。

注意:
1.这时候并不需要Spark Streaming端的receiver了。
2.启动顺序:先启动flume在启动spark streaming。

这确保了比以前的方法更强大的可靠性和容错保证。 但是,这需要配置Flume运行自定义接收器。 以下是配置步骤。

  • flume配置
    1.jar包依赖
groupId = org.apache.spark
artifactId = spark-streaming-flume-sink_2.11
version = 2.3.0

 groupId = org.scala-lang
 artifactId = scala-library
 version = 2.11.8

 groupId = org.apache.commons
 artifactId = commons-lang3
 version = 3.5

2.配置文件

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

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

# Describe the sink
a1.sinks.k1.type = org.apache.spark.streaming.flume.sink.SparkSink
a1.sinks.k1.hostname = localhost
a1.sinks.k1.port = 41414

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

# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
  • 启动
./flume-ng agent \
--name a1 \
--conf $FLUME_HOME/conf \
--conf-file $FLUME_HOME/conf/flume-sparkStreaming-poll \
-Dflume.root.logger=INFO,console

./spark-submit --master local[2] \
--class cn.zhangyu.FlumePull \
--packages org.apache.spark:spark-streaming-flume_2.11:2.2.0 \
--name FlumePull \
/home/hadoop/lib/spark_streaming-1.0-SNAPSHOT.jar \
localhost 41414
  • 输入
telnet localhost 44444

a,a,a,b,b,b,c,c,c,d,d,s
  • 结果:
(d,2)
(b,3)
(s,1)
(a,3)
(c,3)

4 maven中scope属性的

扩展:上面我们说了使用spark-submit提交任务的时候可以使用--packagers,--jars但是这并不是一个很好的方法,我们可以使用maven编译的时候加上需要的类,做法如下:
1.在pom.xml添加

<plugin>
        <artifactId>maven-assembly-pluginartifactId>
        <configuration>
          <archive>
            <manifest>
              <mainClass>mainClass>
            manifest>
          archive>
          <descriptorRefs>
            <descriptorRef>jar-with-dependenciesdescriptorRef>
          descriptorRefs>
        configuration>
      plugin>

Dependency Scope

在POM 4中,dependency中还引入了scope,它主要管理依赖的部署。目前可以使用5个值:

  • compile,缺省值,适用于所有阶段,会随着项目一起发布。
  • provided,类似compile,期望JDK、容器或使用者会提供这个依赖。如servlet.jar。
  • runtime,只在运行时使用,如JDBC驱动,适用运行和测试阶段。
  • test,只在测试时使用,用于编译和运行测试代码。不会随项目发布。
  • system,类似provided,需要显式提供包含依赖的jar,Maven不会在Repository中查找它。

依赖范围控制哪些依赖在哪些classpath 中可用,哪些依赖包含在一个应用中。让我们详细看一下每一种范围:

属性 含义
compile (编译范围) compile是默认的范围;如果没有提供一个范围,那该依赖的范围就是编译范围。编译范围依赖在所有的classpath 中可用,同时它们也会被打包。
provided (已提供范围) provided 依赖只有在当JDK 或者一个容器已提供该依赖之后才使用。例如, 如果你开发了一个web 应用,你可能在编译 classpath 中需要可用的Servlet API 来编译一个servlet,但是你不会想要在打包好的WAR 中包含这个Servlet API;这个Servlet API JAR 由你的应用服务器或者servlet 容器提供。已提供范围的依赖在编译classpath (不是运行时)可用。它们不是传递性的,也不会被打包。
runtime (运行时范围) runtime 依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如,你可能在编译的时候只需要JDBC API JAR,而只有在运行的时候才需要JDBC驱动实现。
test (测试范围) test范围依赖 在一般的编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用。
system (系统范围) system范围依赖与provided 类似,但是你必须显式的提供一个对于本地系统中JAR 文件的路径。这么做是为了允许基于本地对象编译,而这些对象是系统类库的一部分。这样的构件应该是一直可用的,Maven 也不会在仓库中去寻找它。如果你将一个依赖范围设置成系统范围,你必须同时提供一个 systemPath 元素。注意该范围是不推荐使用的(你应该一直尽量去从公共或定制的 Maven 仓库中引用依赖)。

所以我们可以在编译的时候把FlumeUtils这个类加进去:
Flume+Spark Streaming_第2张图片
使用mvn assembly:assembly命令进行编译

你可能感兴趣的:(spark,streaming)