Spark Streaming
StreamingContext流处理的入口
import org.apache.spark._
import org.apache.spark.streaming._
import org.apache.spark.streaming.StreamingContext._
val conf=new SparkConf().setMaster("local[2]").setAppName("streaming demo")
val ssc=new StreamingContext(conf,Seconds(8))
Seconds(8) 为批处理间隔时间

内建流式数据源
$nc -lk 9999 //数据服务器 ssc为StreamingContext对象
#Socket
val lines = ssc.socketTextStream("single", 9999,StorageLevel.MEMORY_AND_DISK_SER_2)
#文件系统
val lines = ssc.textFileStream("hdfs://192.168.37.200:9000/kgc")
#Flume Sink
val ds = FlumeUtils.createPollingStream(streamCtx, [sink hostname], [sink port]);
#Kafka Consumer
val ds = KafkaUtils.createStream(streamCtx, zooKeeper, consumerGrp, topicMap);
Spark Streaming 集成 Flume
Spark Streaming 通过 Push 和 Pull 两种方式对接 Flume 数据源。Push 方式属于推送(由 Flume 向 Spark 推送)而 Pull 属于拉取(Spark 拉取 Flume 的输出)。Push 和 Pull 两者的差别主要体现在Flume Sink 的不同,而 Flume Source 与 Channel 不会受影响
//首先需要额外依赖下面组件:
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-flume_2.11</artifactId>
<version>2.4.4</version>
</dependency>
Push 方式
1、Push 方式在 Spark 端实现并打包
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.flume.{FlumeUtils, SparkFlumeEvent}
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
* 第一步: 编写Flume程序, 配置Avro sink
* 第二步: pom中导入依赖包
* 第三步: 编写Spark程序, 使用 FlumeUtils.createStream 配置数据源
* 第四步: 打成jar包, 上传到集群, 使用spark-submit提交
* 第五步: 启动Flume程序
*/
object Push {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getName).setMaster("local[2]")
val ssc = new StreamingContext(conf,Seconds(5))
// 1.加载数据源
val flumeStream: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createStream(ssc,"single",9999)
val lines: DStream[String] = flumeStream.map(x=>new String(x.event.getBody.array()).trim)
// 2.对数据进行处理[WordCount]
val result: DStream[(String, Int)] = lines.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)
// 3.数据写出[print]
result.print()
// 4.启动程序
ssc.start()
ssc.awaitTermination()
}
}
2、编写Flume配置文件avro.sink.conf,Flume 架构为:netcat->memory->avro
agent.sources = s1
agent.channels = c1
agent.sinks = sk1
agent.sources.s1.type = netcat
agent.sources.s1.bind = localhost
agent.sources.s1.port = 5678
agent.sources.s1.channels = c1
agent.sinks.sk1.type = avro
agent.sinks.sk1.hostname =single
agent.sinks.sk1.port = 9999
agent.sinks.sk1.channel = c1
agent.channels.c1.type = memory
agent.channels.c1.capacity = 1000
3、先启动Spark应用程序
spark-submit --class Spark_Streaming.Push SparkLearn-1.0.jar
//Spark_Streaming.Push 包名+类名
//SparkLearn-1.0.jar jar包名
4、再启动 Flume Agent
bin/flume-ng agent --name agent -f /root/avro.sink.conf -Dflume.root.logger=INFO,console
5.启动 nc 或者 Telnet 连接 Socket,向 Flume nectcat Source 发送数据,并在
Spark 流应用程序中观察结果
Pull 方式
当采用 Pull 方式时,Flume 架构为:netcat->memory->SparkSink。Sink 全类名为 org.apache.spark.streaming.flume.sink.SparkSink,在Flume 的配置文件中添加。还需要将以下组件放在$FLUME_HOME/lib 下,若存在相同jar包,可将之前的删除或者修改名称,避免jar包冲突

1.编写 Pull 方式获取 Input DStream 代码并打包
object Pull {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getName).setMaster("local[4]")
val ssc = new StreamingContext(conf,Seconds(5))
val flumeStream=FlumeUtils.createPollingStream(ssc,"single",9999,StorageLevel.MEMORY_ONLY_SER_2)
val lines: DStream[String] = flumeStream.map(x=>new String(x.event.getBody.array()).trim)
// 2.对数据进行处理[WordCount]
val result: DStream[(String, Int)] = lines.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)
// 3.数据写出[print]
result.print()
// 4.启动程序
ssc.start()
ssc.awaitTermination()
}
}
2.编写 Flume 配置文件sink_spark-pull.conf
agent.sources = s1
agent.channels = c1
agent.sinks = sk1
agent.sources.s1.type = netcat
agent.sources.s1.bind = localhost
agent.sources.s1.port = 5678
agent.sources.s1.channels = c1
agent.sinks.sk1.type = org.apache.spark.streaming.flume.sink.SparkSink
agent.sinks.sk1.hostname =single
agent.sinks.sk1.port = 9999
agent.sinks.sk1.channel = c1
agent.channels.c1.type = memory
agent.channels.c1.capacity = 1000
3.先启动 Flume Agent
bin/flume-ng agent --name agent -f /root/sink_spark-pull.conf -Dflume.root.logger=INFO,console
4.再启动 Spark 流应用程序
spark-submit --class Spark_Streaming.Pull SparkLearn-2.0.jar
5.启动 nc 或者 Telnet 连接 Socket,向 Flume nectcat Source 发送数据,并在
Spark 流应用程序中观察结果。
两种方式对比如下:
- 对第三方 jar 的依赖:Pull 方式时 Flume 需要调用 Spark 实现的 Sink,所以多依赖一个 spark-streaming-flume-sink,Flume 与 Spark 两边同时依赖。
- 启动顺序:Push 方式时 Flume 虽然不依赖 Spark,但 avro sink 需要一个 avro
服务,这个服务应该先启动。所以 Spark 应先启动。而 Pull 则与之相反。
Spark Streaming 集成 Kafka
Direct 方式采用 Kafka 简单的 consumer api 方式来读取数据,此种方式不再需要专门 Receiver 来持续不断读取数据。当 batch 任务触发时,由 Executor 读取数据,并参与到其他 Executor 的数据计算过程中去。driver 来决定读取多少 offsets,并将 offsets 交由 checkpoints 来维护。将触发下次 batch 任务,再由 Executor 读取 Kafka 数据并计算。
import org.apache.spark.SparkConf
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, KafkaUtils, LocationStrategies}
import org.apache.spark.streaming.{Seconds, StreamingContext}
object kafka_spark {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getName).setMaster("local[4]").set("spark.serializer","org.apache.spark.serializer.KryoSerializer")
val ssc = new StreamingContext(conf, Seconds(5))
ssc.checkpoint(".")
val kafkaParams = Map(
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG->"192.168.37.210:9092",
ConsumerConfig.GROUP_ID_CONFIG -> "test1",
ConsumerConfig.MAX_POLL_RECORDS_CONFIG->"500",
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG->classOf[StringDeserializer],
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG->classOf[StringDeserializer],
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG->"earliest"
)
//LocationStrategies.PreferConsistent :每个 Spark Executor作为一个Consumer,Executor与Kafka Partition一对一 。大多数情况下使用,主要是为了均匀分布。
//ConsumerStrategies.Subscribe:允许订阅固定的主题集合
val messages = KafkaUtils.createDirectStream[String, String](ssc,LocationStrategies.PreferConsistent,
ConsumerStrategies.Subscribe[String, String](Set("test"),kafkaParams))
messages.map(_.value()).flatMap(_.split(" ")).map(word => (word, 1))
.reduceByKey(_+_).print()
ssc.start()
ssc.awaitTermination()
}
}
Spark Streaming处理带状态的数据
DStream转换操作包括无状态转换和有状态转换,updateStateByKey属于有状态转换,可以跟踪状态的变化
object Spark_Streaming {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount")
val ssc = new StreamingContext(sparkConf, Seconds(5))
def updateFunction(currentValues: Seq[Int], preValues: Option[Int]): Option[Int] = {
val curr = currentValues.sum
val pre = preValues.getOrElse(0)
Some(curr + pre)
}
ssc.checkpoint(".")
val lines = ssc.socketTextStream("single", 9999)
val result = lines.flatMap(_.split(" ")).map((_, 1))
val state = result.updateStateByKey(updateFunction)
state.print()
ssc.start()
ssc.awaitTermination()
}
}
本文详细介绍了SparkStreaming如何与Flume和Kafka集成,包括Push和Pull两种方式对接Flume,以及SparkStreaming直接消费Kafka数据。Push方式中,Flume向Spark推送数据,而Pull方式则是Spark从Flume拉取数据。对于Kafka,Spark采用Direct方式,Executor直接读取数据参与计算。此外,还提到了SparkStreaming中的状态处理,如updateStateByKey。
2099

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



