streaming
数据处理延迟的长短,实时和离线处理区别
实时数据处理:毫秒级别
离线数据处理:小时 or 天级别
Sparkstreaming 是一个准实时(秒,分钟),微批次(时间)的数据处理框架
指定间隔来进行 采集数据进行执行
scala采集器,3秒间隔接收,同时间段的都会被采集
def main(args: Array[String]): Unit = {
val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
val value: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 9999)
val words: DStream[String] = value.flatMap(_.split(" "))
val wordToOne: DStream[(String, Int)] = words.map((_, 1))
val wordCount: DStream[(String, Int)] = wordToOne.reduceByKey(_ + _)
wordCount.print()
// sparkStreaming采集器是不可以直接就关闭
// 如果main方法执行完毕,应用程序也会自动结束,所以也不能让main结束
// ssc.stop()
// 1.启动采集器
ssc.start()
// 2.等待采集器的关闭
ssc.awaitTermination()
}
自定义数据采集器
def main(args: Array[String]): Unit = {
val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
val value: ReceiverInputDStream[String] = ssc.receiverStream(new MyReceiver())
value.print()
ssc.start()
ssc.awaitTermination()
}
class MyReceiver extends Receiver[String](StorageLevel.MEMORY_ONLY){
private var flg = true
override def onStart(): Unit = {
new Thread(new Runnable {
override def run(): Unit = {
while(flg){
val message = "采集的数据为:" + new Random().nextInt(10).toString
store(message)
Thread.sleep(500)
}
}
}).start()
}
override def onStop(): Unit = {
flg=false
}
}
读取kafka数据
spark-streaming一般都是和kafka来进行工作
首先我们需要集群启动kafka
bin/kafka-topics.sh --bootstrap-server master:9092 --list
# create 创建 topic创建top的名称 partitions分区数 replication-factor复制因子数量
bin/kafka-topics.sh --bootstrap-server master:9092 --create --topic cdl --partitions 3 --replication-factor 1
#启动topic
bin/kafka-console-producer.sh --broker-list master:9092 --topic cdl
这边kafka我只启动了一个
def main(args: Array[String]): Unit = {
val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
val kafkaPara: Map[String, Object] = Map[String, Object](
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> "master:9092",
ConsumerConfig.GROUP_ID_CONFIG -> "cdl",
"key.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer",
"value.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer"
)
val kafkaDataDS: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String, String](
ssc,
LocationStrategies.PreferConsistent,
ConsumerStrategies.Subscribe[String, String](Set("cdl"), kafkaPara)
)
kafkaDataDS.map(_.value()).print()
ssc.start()
ssc.awaitTermination()
}
然后再启动的topic下输入任意字符,查看控制台接收到即可成功
状态操作
进行map,groupbykey等操作都是无状态操作,只针对当前周期内的数据进行计算,过了之前的就没了
如果并保存了操作就是有状态操作
有状态操作
def main(args: Array[String]): Unit = {
val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
ssc.checkpoint("cp")// 指定检查点落地路径
val value: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 9999)
val v: DStream[(String, Int)] = value.map((_, 1))
//使用有状态操作时候需要指定检查点路径
// updateStateByKey:根据key数据的状态进行更新
// 两个参数
// 第一个值表示相同key的value数据
// 第二个值表示缓存区相同key的数据
v.updateStateByKey(
(seq:Seq[Int],buff:Option[Int])=>{
val newCount = buff.getOrElse(0)+seq.sum
Option(newCount)
}
).print()
ssc.start()
ssc.awaitTermination()
}
无状态操作transform
transform可以直接获取到底层的rdd,和上面直接用map操作不同的是,transform可以周期性的源源不断的
def main(args: Array[String]): Unit = {
val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
ssc.checkpoint("cp")// 指定检查点落地路径
val value: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 9999)
value.transform(rdd=>rdd.map(rdd=>rdd))
ssc.start()
ssc.awaitTermination()
}
无状态操作join
join可以把两个不同数据源的数据拼接在一起
有状态操作window
两个参数:窗口时长 , 滑动步长
窗口时长:计算内容的时间范围
滑动步长:隔多久触发一次计算
且必须要是采集周期的整数倍数
def main(args: Array[String]): Unit = {
val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
ssc.checkpoint("cp")// 指定检查点落地路径
val value: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 9999)
val value1: DStream[(String, Int)] = value.map((_, 1))
// 传入的窗口时长和滑动步长相同的时候,就可以避免重复数据
// 而且都必须是上方ssc的采集周期的整数倍数
val wd: DStream[(String, Int)] = value1.window(Seconds(6), Seconds(6))
val rd: DStream[(String, Int)] = wd.reduceByKey(_ + _)
ssc.start()
ssc.awaitTermination()
}
更多方法
window(windowLength,silidenterval):基于对源DStream窗口的批次进行计算返回一个新的Dstream
countByWindow(windowLength,silidenterval):返回一个滑动窗口计数流中的元素个数
reduceByWindow(func,windowLength,silidenterval):通过使用自定义函数整合滑动区间流元素来创建新的单元素流
reduceByKeyAndWindow(func,windowLength,silidenterval,[numTesks]):当在一个(K,V)对的DStream上调用此函数,会返回一个新(K,V)对的DStream,此处通过对滑动窗口中批次数据使用reduce函数来整合每个key的value值
reduceByKeyAndWindow(func,invFunc,windowLength,silidenterval,[numTesks]):此函数是上面函数的变化版本,多了一个invFUnc的参数,用来删掉不需要的数据,第一个参数用来传入进行计算数据
DStream输出
上方的操作都是有关DStream输入的操作,我们当中执行的print方法就是属于DStream输出的操作
在进行流输入的时候数据是源源不断输入的,在运行过程中 必须要有输出操作,否则就会报错
输出操作如下:
print():在运行流程序的驱动结点上答应DStream中每一批次数据的最开始10个元素,这用于开发调试
saveAsTextFiles(prefix,[suffix]):text文件形式存储,每一批次存储文件名指定prefix.suffix
saveAsObjectFiles(prefix,[suffix]):以java对象序列化的方式保存数据
saveAsHadoopFiles(prefix,[suffix]):保存成hadoop files
foreachRDD(func):这是最通用的输出操作,通过函数func对每一个rdd进行实现,将rdd推送到外部系统,存入数据库或者文件等,可以重用spark实现的所有行动操作,要注意:
1.连接不能卸载driver层面(序列化的原因)
2.如果写在foreach则每个rdd中的每一条数据都创建,得不偿失
3.增加foreachPartition,在分区创建获取
优雅关闭程序
在流运行的过程中,有时候需要关闭进行升级
ssc.stop(true) // 进行强制关闭
ssc.stop(true,true) // 进行优雅的关闭,且会执行完当前其他正在执行的任务
// 通过第三方程序增加关闭状态
new Thread(new Runnable {
override def run(): Unit = {
// 优雅的关闭
// 我们可以通过以下方式
// Mysql:
// Redis:
// ZK:
// HDFS:
// 获取状态后执行代码进行关闭
val state = ssc.getState()
if(state == StreamingContextState.ACTIVE){
ssc.stop(true,true)
System.exit(0) // 关闭线程
}
}
})
当我们再运行过程之中指定了检查点的路径,再次启动的时候可以再次读取检查点的路径,如果读取不到就重新创建一个
val ssc = StreamingContext.getActiveOrCreate("cp",()=>{
val streaming: SparkConf = new SparkConf().setMaster("local[*]").setAppName("streaming")
val ssc = new StreamingContext(streaming, Seconds(3))// Seconds(3) 3秒周期
val value: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 9999)
val value1: DStream[(String, Int)] = value.map((_, 1))
value.print()
ssc
})
ssc.checkpoint("cp") // 保存检查点
ssc.start()
ssc.awaitTermination()