SparkStreaming+Flume流计算

SparkStreaming + Flume的方式是一个早期的形式,在新版本的Spark中,并不提倡直接与Flume去对接的,但是有的时候我们任然需要这样的形式去处理数据

Spark整合Flume的数据在Spark中有两种方式Poll拉与Push推,两种模式对比起来优先使用Poll模式

拉模式就是Spark提供了一个sink,SparkStreaming主动的自己去channel里面取数据,根据自身条件去获取数据,稳定性好。

推模式,是Flume作为缓存,存有数据。并监听Spark,如果Spark可达,就将数据push过去。(简单,耦合要低),缺点是SparkStreaming 程序没有启动的话,Flume端会报错,同时可能会导致Spark Streaming 程序来不及消费丢失数据的情况。

首先介绍Poll拉数据方式

需要导入一个jar,如下

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming-flume_2.11</artifactId>
    <version>2.0.2</version>
</dependency>

Poll拉的时候要注意,Flume的版本要在1.6以上,我用的是1.8,且需要确保flume的lib目录下拥有两个jar,我的版本如下

scala-library-2.11.8.jar 
spark-streaming-flume-sink_2.11-2.0.2.jar

scala-library-2.11.8.jar在放入lib之后要改个名字,改为scala-library-2.10.5.jar.BAK

下面编写Flume的agent,source用的是nc工具,大家也可以自己用自己的agent

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

#  source需要改动的是源和目的地,此处是源,使用nc工具
a1.sources.r1.type = netcat
a1.sources.r1.bind = localhost
a1.sources.r1.port = 44444

# Describe the sink 需要改动的是源和目的地,此处是目的地,且为Spark配置一个连接的地址
a1.sinks.k1.type = org.apache.spark.streaming.flume.sink.SparkSink
a1.sinks.k1.hostname=192.168.182.146
a1.sinks.k1.port = 8888
a1.sinks.k1.batchSize= 2000 


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

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

启动flume

bin/flume-ng agent --conf conf --conf-file conf/socket-source.properties --name a1 -Dflume.root.logger=INFO,console

flume启动之后,就可以通过前面设置的nc端口产生数据了,Poll方式不会报错,数据会暂存在channel中

下面我们来编写并启动代码

package com.stream

import org.apache.spark.{
    
    SparkConf}
import org.apache.spark.streaming.flume.{
    
    FlumeUtils}
import org.apache.spark.streaming.{
    
    Seconds, StreamingContext}

object StreamFromFlume {
    
    
  def main(args: Array[String]): Unit = {
    
    
    val conf = new SparkConf().setAppName("StreamFromKafka").setMaster("local[2]")
    val scc = new StreamingContext(conf,Seconds(10))
    //设置checkpoint,可以忽略
    scc.checkpoint("D:\\checkpoint")
    // 从flume中拉取数据 这里的ip和端口是你agent中sink配置的
    val flumeStream = FlumeUtils.createPollingStream(scc,"192.168.182.146",8888)
    
    val lineStream= flumeStream.map(x=>new String(x.event.getBody.array()))

    //实现单词汇总
    val result = lineStream.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)

    result.print()
    scc.start()
    scc.awaitTermination()
  }

}

之后介绍Push推数据方式

Push推数据是Flume占据了主动所以,不是很建议,工作中也不大可能会用到,毕竟数据的丢失对一个数据项目来说是一个很难受的时候

Push的时候一定要先启动Spark,其次Push是Flume做主动,使用的是Flume自身拥有的avro系列化,该方式其实可以给大部分的框架推送数据,不止Spark

接收代码和Poll的差别就是API变了

package com.stream

import org.apache.spark.{
    
    SparkConf}
import org.apache.spark.streaming.flume.{
    
    FlumeUtils}
import org.apache.spark.streaming.{
    
    Seconds, StreamingContext}

object StreamFromFlume {
    
    
  def main(args: Array[String]): Unit = {
    
    
    val conf = new SparkConf().setAppName("StreamFromKafka").setMaster("local[2]")
    val scc = new StreamingContext(conf,Seconds(10))
    //设置checkpoint,用来提高数据消费的安全
    scc.checkpoint("D:\\checkpoint")
    // ip和port任然是Flume配置文件中的
    val flumeStream: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createStream(scc,"192.168.182.146",8888,StorageLevel.MEMORY_AND_DISK)
    
    val lineStream= flumeStream.map(x=>new String(x.event.getBody.array()))

    //实现单词汇总
    val result = lineStream.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)

    result.print()
    scc.start()
    scc.awaitTermination()
  }

}

Spark Push方式启动之后会正常等待Flume推送数据,下面我们开始准备Flume端

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

#  source需要改动的是源和目的地,此处是源,使用nc工具
a1.sources.r1.type = netcat
a1.sources.r1.bind = localhost
a1.sources.r1.port = 44444

# Describe the sink 需要改动的是源和目的地
a1.sinks.k1.type = avro
a1.sinks.k1.hostname=192.168.182.146
a1.sinks.k1.port = 8888


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

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

在启动Flume之后我们去查看Spark就会发现随着我们在nc工具中数据的输入,Spark哪里就有可对应的输出


最后做个补充如果你在运行两种模式中途发生如下错误
org.apache.avro.AvroRuntimeException: Unknown datum type: java.lang.Exception: 
java.lang.NoClassDefFoundError: Could not initialize class 
org.apache.spark.streaming.flume.sink.EventBatch 

这个错误是因为你的Flume的avro系列化版本和Spark的不兼容,你需要另外导入,在你的pom文件中导入如下jar

<dependency>
	<groupId>org.apache.avro</groupId>
	<artifactId>avro</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
	<groupId>org.apache.avro</groupId>
	<artifactId>avro-ipc</artifactId>
	<version>1.8.2</version>
</dependency>

并且将这两个个jar拷贝到Flume的lib目录下,将lib中原有的该jar删掉

猜你喜欢

转载自blog.csdn.net/dudadudadd/article/details/115005546