官网地址: Flume Integration Guide
1、Spark Streaming 2.3.1适配 Flume 1.6.0,在Spark Streaming 2.3.0之后对flume的支持已被标记为过时。主要由于flume直接对接Spark Streaming 会造成Spark Streaming压力过大,特别是高峰期的时候(在之间加一层消息队列会好得多)。但由于很多公司可能仍然在用,故简单做一下介绍。
2、有两种方式可以让Spark Streaming集成Flume并从Flume接受数据。以下分别介绍这两种方式。
一、基于Flume的Push模式(Flume-style Push-based Approach)
这种方式Spark Streaming会建立一个Receiver,这个Receiver起到一个相当于Flume的Avro Agent的作用,Flume可以将数据推送到这个Receiver。以下是详细的配置步骤。
1、一般要求
在你的集群中选择一台机器,这台机器需要满足一下条件:
A.当Spark Streaming+Flume的应用程序运行时,有一个Spark的Worker节点运行在这台机器上。
B.Flume通过配置后可以将数据推送到这台机器的端口上。
2、配置Flume
通过以下的配置文件可以将数据发送到Avro Sink。
agent.sinks = avroSink
agent.sinks.avroSink.type = avro
agent.sinks.avroSink.channel = memoryChannel
agent.sinks.avroSink.hostname = <所选机器的IP>
agent.sinks.avroSink.port = <所选机器的端口>
3、配置Spark Streaming应用程序
A.添加依赖
<dependency>
<groupId>org.apache.spark </groupId>
<artifactId>spark-streaming-flume_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
B.在Streaming应用程序的代码中,导入一个FlumeUtils类并创建input DStream。
import org.apache.spark.streaming.flume._
val flumeStream = FlumeUtils.createStream(streamingContext, [所选机器ip], [所选机器端口])
4、测试
A.直接运行代码
package com.ruozedata.streaming
import org.apache.spark.SparkConfimport org.apache.spark.streaming.flume.FlumeUtils
import org.apache.spark.streaming.{Seconds, StreamingContext}
object FlumePushApp {
def main(args: Array[String]) {
val Array(hostname, port) = args
val sparkConf = new SparkConf()
.setAppName("FlumePushApp")
.setMaster("local[2]")
val ssc = new StreamingContext(sparkConf, Seconds(10))
val lines = FlumeUtils.createStream(ssc,hostname,port.toInt)
/*由于createStream返回的DStream类型为SparkFlumeEvent,而不是String,故此时split方法无法使用
*为了能够使用split,我们执行了以下的map操作
*/
lines.map(x => new String(x.event.getBody.array()).trim)
.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).print()
ssc.start()
ssc.awaitTermination()
}
}
//以上代码运行会出错,因为args没有传参数。
传参流程:右上角FlumePushApp-->Edit Configurations-->program arguments-->填写满足条件的参数

接着:

B.打成jar包上传运行
先启动spark-submit
./spark-submit --master local[2] \
--class com.ruozedata.streaming.FlumePullApp \
--packages org.apache.spark:spark-streaming-flume_2.11:2.2.0 \ //相当于添加依赖,需要集群能够访问外网
--name FlumePushApp \
/home/hadoop/lib/train-scala-1.0.jar \
localhost 41414
再启动flume
./flume-ng agent \
--name a1 \
--conf $FLUME_HOME/conf \
--conf-file $FLUME_HOME/conf/nc-memory-flume.conf \
-Dflume.root.logger=INFO,console
在另一台客户端执行 telnet localhost 44444,此时在该客户端输入数据,另一客户端会实时打印出处理结果
//mvn clean packages 打包只包含源码不包含依赖包
//以上解决方式需要集群能够访问外网,不能访问外网时可参照以下步骤
a。把不所有需要打包进来的依赖全部添加<scope>provided</scope>
b。在pom.xml文件中添加plugin
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass></mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
c。编译时使用 mvn assembly:assembly来打包
二、基于自定义sink的pull模式(Pull-based Approach using a Custom Sink)
1、一般要求
不同于Flume直接将数据推送到Spark Streaming中,这种方法自定义一个满足以下条件的Flume Sink:
- Flume将数据推送到sink中,并且数据保持buffered状态
- Spark Streaming使用一个可靠的Flume接收器(reliable Flume receiver )和转换器(transaction)从sink拉取数据.只要当数据被接收并且被Spark Streaming备份后,转换器才运行成功.
2、添加依赖
groupId = org.apache.spark
artifactId = spark-streaming-flume-sink_2.11
version = 2.3.1
groupId = org.scala-lang
artifactId = scala-library
version = 2.11.8
groupId = org.apache.commons
artifactId = commons-lang3
version = 3.5
3、配置flume
a1.sinks.k1.type = org.apache.spark.streaming.flume.sink.SparkSink
a1.sinks.k1.hostname = localhost
a1.sinks.k1.port = 41414
4、测试
package com.ruozedata.streaming
import org.apache.spark.SparkConf
import org.apache.spark.streaming.flume.FlumeUtils
import org.apache.spark.streaming.{Seconds, StreamingContext}
object FlumePullApp {
def main(args: Array[String]) {
val Array(hostname, port) = args
val sparkConf = new SparkConf()
.setAppName("FlumePullApp")
.setMaster("local[2]")
val ssc = new StreamingContext(sparkConf, Seconds(10))
val lines = FlumeUtils.createPollingStream(ssc, hostname,port.toInt)
lines.map(x => new String(x.event.getBody.array()).trim)
.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).print()
ssc.start()
ssc.awaitTermination()
}
}
5、启动
先启动Flume
./flume-ng agent \
--name a1 \
--conf $FLUME_HOME/conf \
--conf-file $FLUME_HOME/conf/nc-memory-flume2.conf \
-Dflume.root.logger=INFO,console
然后再另一客户端启动 telnet localhost 44444
最后启动Spark Streaming
./spark-submit --master local[2] \
--class com.ruozedata.streaming.FlumePullApp \
--packages org.apache.spark:spark-streaming-flume_2.11:2.2.0 \
--name FlumePushApp \
/home/hadoop/lib/train-scala-1.0.jar \
localhost 41414
此时在telnet端输入数据,立马出处理结果。
第二种方式更好
最后来两个与题目无关的Spark Streaming小例子
1、黑名单过滤
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.ListBuffer/**
* Created by ruozedata on 2018/4/15.
*/
object FilterApp {
def main(args: Array[String]) {
val sparkConf = new SparkConf()
.setAppName("FilterApp")
.setMaster("local[2]")
val sc = new SparkContext(sparkConf)
// (骚马,true)
val blackTuple = new ListBuffer[(String,Boolean)]
blackTuple.append(("sm",true)) //true为了方便join
val blacksRDD = sc.parallelize(blackTuple) //将数组转成RDD
// 准备测试数据: log
val input = new ListBuffer[(String,String)]
input.append(("su","20180808,su,M,20"))
input.append(("kk","20180808,kk,M,20"))
input.append(("sm","20180808,sm,M,20"))
val inputRDD = sc.parallelize(input)
// TODO... 过滤掉黑名单用户
val joinRDD = inputRDD.leftOuterJoin(blacksRDD)
joinRDD.filter(x => {
x._2._2.getOrElse(false) != true
}).map(_._2._1).foreach(println) //x._2._2为true或none,x._2._1为日志本身
sc.stop()
}
}
2、改造例子1,将其使用Spark Streaming来完成
package com.ruozedata.streaming
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
object StreamingFilterApp {
def main(args: Array[String]) {
val sparkConf = new SparkConf()
.setAppName("SocketWordCountApp")
.setMaster("local[2]")
val ssc = new StreamingContext(sparkConf, Seconds(10))
val blacks = List("sm","su")
val blacksRDD = ssc.sparkContext.parallelize(blacks).map(x=>(x,true))
val lines = ssc.socketTextStream("hadoop000",9997)
// (su, log of su)
val clickLogDstream = lines.map(x => (x.split(",")(1), x)).transform(rdd => {
rdd.leftOuterJoin(blacksRDD)
.filter(x => {
// _2 : (string,option) _2:option
x._2._2.getOrElse(false) != true
}).map(_._2._1) //x._2._2为true或none,x._2._1为日志本身
})
clickLogDstream.print()
ssc.start()
ssc.awaitTermination()
}
}
本文介绍了如何在Spark Streaming中使用Flume作为数据源,包括两种集成方式:基于Flume Push模式和自定义Sink的Pull模式。在Push模式中,Spark Streaming创建Receiver接收Flume推送的数据;而在Pull模式下,Spark Streaming通过自定义Sink拉取数据。文章还提供了详细的配置和测试步骤。
460

被折叠的 条评论
为什么被折叠?



