流处理主要有3种应用场景:无状态操作、window操作、状态操作。

reduceByKeyAndWindow

import kafka.serializer.StringDecoder
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.SQLContext
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.streaming._
import org.apache.spark.{SparkContext, SparkConf} object ClickStream {
def main (args: Array[String]){
// 屏蔽不必要的日志显示在终端上
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF) //创建SparkConf对象,设置应用程序的名称,在程序运行的监控界面可以看到名称
val conf = new SparkConf().setAppName("ClickStream").setMaster("local[*]")
val sc = new SparkContext(conf) //此处设置Batch Interval是在Spark Streaming中生成基本Job的时间单位,窗口和滑动时间间隔一定是该Batch Interval的整数倍
val ssc = new StreamingContext(sc, Seconds(args().toLong)) //由于用到了窗口函数,需要复用前面的RDD,必须checkpoint,注意复用的RDD之间是没有任何关系的
ssc.checkpoint(args()) val topics = Set("clickstream") //所要获取数据在kafka上的主题
val brokers = "yz4203.hadoop.data.sina.com.cn:19092,yz4202.hadoop.data.sina.com.cn:19092,10.39.4.212:19092,10.39.4.201:19092"
val kafkaParams = Map[String, String]("metadata.broker.list" -> brokers)
//val offset = "largest" //values: smallest, largest ,控制读取最新的数据,还是旧的数据, 默认值为largest //从Spark1.3开始,我们能够使用如下方式高效地从kafka上获取数据
val kvsTemp = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topics)
val kvs = kvsTemp.map(line => line._2) //第一部分是null为key,第二部分才是所需数据,为string类型 //根据需求对流进来的数据进行清洗、转换等处理
val data = kvs.map(_.split("\\t")).filter(_() == "finance").map(_()).map(_.split("\\?")()).filter(! _.contains("iframe")).map((_, )) //滑动窗口长度为1小时,滑动间隔为10分钟,这会得到过去1小时内,url和pv的对应关系
//val pvWindow = data.reduceByKeyAndWindow((v1: Int, v2: Int) => v1+v2, Minutes(60), Minutes(10)) //滑动窗口长度为1小时,滑动间隔为10分钟,这同样会得到过去1小时内,url和pv的对应关系,只不过这是加新减旧,第一个参数加上新的,第2个参数,减去上一个batch的。
//和上一个版本的reduceByKeyAndWindow每次都会重新算相比(叠加方式),这种方式(增量方式)更加高效优雅
val pvWindow = data.reduceByKeyAndWindow(_ + _, _ - _, Minutes(), Minutes())
pvWindow.print() ssc.start() // Start the computation
ssc.awaitTermination() // Wait for the computation to terminat
ssc.stop(true, true) //优雅地结束
}
}

countByValueAndWindow

countByValueAndWindow的源码如下所示:

 /**
* Return a new DStream in which each RDD contains the count of distinct elements in
* RDDs in a sliding window over this DStream. Hash partitioning is used to generate
* the RDDs with `numPartitions` partitions (Spark's default number of partitions if
* `numPartitions` not specified).
* @param windowDuration width of the window; must be a multiple of this DStream's
* batching interval
* @param slideDuration sliding interval of the window (i.e., the interval after which
* the new DStream will generate RDDs); must be a multiple of this
* DStream's batching interval
* @param numPartitions number of partitions of each RDD in the new DStream.
*/
def countByValueAndWindow(
windowDuration: Duration,
slideDuration: Duration,
numPartitions: Int = ssc.sc.defaultParallelism)
(implicit ord: Ordering[T] = null)
: DStream[(T, Long)] = ssc.withScope {
this.map((_, 1L)).reduceByKeyAndWindow(
(x: Long, y: Long) => x + y,
(x: Long, y: Long) => x - y,
windowDuration,
slideDuration,
numPartitions,
(x: (T, Long)) => x._2 != 0L
)
}

reduceByWindow

reduceByWindow的源码如下所示:

/**
* Return a new DStream in which each RDD has a single element generated by reducing all
* elements in a sliding window over this DStream. However, the reduction is done incrementally
* using the old window's reduced value :
* 1. reduce the new values that entered the window (e.g., adding new counts)
* 2. "inverse reduce" the old values that left the window (e.g., subtracting old counts)
* This is more efficient than reduceByWindow without "inverse reduce" function.
* However, it is applicable to only "invertible reduce functions".
* @param reduceFunc associative and commutative reduce function
* @param invReduceFunc inverse reduce function; such that for all y, invertible x:
* `invReduceFunc(reduceFunc(x, y), x) = y`
* @param windowDuration width of the window; must be a multiple of this DStream's
* batching interval
* @param slideDuration sliding interval of the window (i.e., the interval after which
* the new DStream will generate RDDs); must be a multiple of this
* DStream's batching interval
*/
def reduceByWindow(
reduceFunc: (T, T) => T,
invReduceFunc: (T, T) => T,
windowDuration: Duration,
slideDuration: Duration
): DStream[T] = ssc.withScope {
this.map((, _))
.reduceByKeyAndWindow(reduceFunc, invReduceFunc, windowDuration, slideDuration, )
.map(_._2)
}

countByWindow

countByWindow的源码如下所示:

 /**
* Return a new DStream in which each RDD has a single element generated by counting the number
* of elements in a sliding window over this DStream. Hash partitioning is used to generate
* the RDDs with Spark's default number of partitions.
* @param windowDuration width of the window; must be a multiple of this DStream's
* batching interval
* @param slideDuration sliding interval of the window (i.e., the interval after which
* the new DStream will generate RDDs); must be a multiple of this
* DStream's batching interval
*/
def countByWindow(
windowDuration: Duration,
slideDuration: Duration): DStream[Long] = ssc.withScope {
this.map(_ => 1L).reduceByWindow(_ + _, _ - _, windowDuration, slideDuration)
}

由此可见,countByValueAndWindow、reduceByWindow、countByWindow的底层实现都是“加新减旧”版本的reduceByKeyAndWindow。

上面,求出了每一小时窗口内的Url和Pv的对应关系,如果想求出相同的Url在上一个窗口的Pv和本次窗口的Pv的比值,那么这时侯updateStateByKey,mapWithState就粉墨登场了。由于updateStateByKey和mapWithState二者之间有10倍左右的性能差异。

这里,只涉及mapWithState。

mapWithState

import kafka.serializer.StringDecoder
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.SQLContext
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.streaming._
import org.apache.spark.{SparkContext, SparkConf} object ClickStream {
def main (args: Array[String]){
// 屏蔽不必要的日志显示在终端上
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF) //创建SparkConf对象,设置应用程序的名称,在程序运行的监控界面可以看到名称
val conf = new SparkConf().setAppName("ClickStream").setMaster("local[*]")
val sc = new SparkContext(conf) //此处设置Batch Interval是在Spark Streaming中生成基本Job的时间单位,窗口和滑动时间间隔一定是该Batch Interval的整数倍
val ssc = new StreamingContext(sc, Seconds(args().toLong)) //由于用到了窗口函数,需要复用前面的RDD,必须checkpoint,注意复用的RDD之间是没有任何关系的
ssc.checkpoint(args()) val topics = Set("clickstream") //所要获取数据在kafka上的主题
val brokers = yz4207.hadoop.data.sina.com.cn:19092,yz4203.hadoop.data.sina.com.cn:19092,yz4202.hadoop.data.sina.com.cn:19092,10.39.4.212:19092,10.39.4.201:19092"
val kafkaParams = Map[String, String]("metadata.broker.list" -> brokers)
//val offset = "largest" //values: smallest, largest ,控制读取最新的数据,还是旧的数据, 默认值为largest //从Spark1.3开始,我们能够使用如下方式高效地从kafka上获取数据
val kvsTemp = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topics)
val kvs = kvsTemp.map(line => line._2) //第一部分是null为key,第二部分才是所需数据,为string类型 //根据需求对流进来的数据进行清洗、转换等处理
val data = kvs.map(_.split("\\t")).filter(_() == "finance").map(_()).map(_.split("\\?")()).filter(! _.contains("iframe")).map((_, )) //滑动窗口长度为1小时,滑动间隔为10分钟,这会得到过去1小时内,url和pv的对应关系
//val pvWindow = data.reduceByKeyAndWindow((v1: Int, v2: Int) => v1+v2, Minutes(60), Minutes(10)) //滑动窗口长度为1小时,滑动间隔为10分钟,这同样会得到过去1小时内,url和pv的对应关系,只不过这是加新减旧,第一个参数加上新的,第2个参数,减去上一个batch的。和上一个版本的reduceByKeyAndWindow每次都会重新算相比(叠加方式),
//这种方式(增量方式)更加高效优雅
val pvWindow = data.reduceByKeyAndWindow(_ + _, _ - _, Minutes(), Minutes()) //key是K, value是新值,state是原始值(本batch之前的状态值)。这里你需要把state更新为新值
val mappingFunc = (key: String, value: Option[Int], state: State[Int]) => {
val currentPV = value.getOrElse()
val output = (key, currentPV, state.getOption().getOrElse())
state.update(currentPV)
output
} //StateSpec只是一个包裹,实际操作仍然是定义的mappingFunc函数
val urlPvs = pvWindow.mapWithState(StateSpec.function(mappingFunc)) //url,当前batch的PV,上一个batch的PV
urlPvs.print() ssc.start() // Start the computation
ssc.awaitTermination() // Wait for the computation to terminat
ssc.stop(true, true) //优雅地结束
}
}

Spark Streaming之窗口函数和状态转换函数的更多相关文章

  1. Spark Streaming揭秘 Day14 State状态管理

    Spark Streaming揭秘 Day14 State状态管理 今天让我们进入下SparkStreaming的一个非常好用的功能,也就State相关的操作.State是SparkStreaming ...

  2. Spark Streaming之六:Transformations 普通的转换操作

    与RDD类似,DStream也提供了自己的一系列操作方法,这些操作可以分成四类: Transformations 普通的转换操作 Window Operations 窗口转换操作 Join Opera ...

  3. java笔记----线程状态转换函数

    注意:stop().suspend()和 resume()方法现在已经不提倡使用,这些方法在虚拟机中可能引起“死锁”现象.suspend()和 resume()方法的替代方法是 wait()和 sle ...

  4. 周期性清除Spark Streaming流状态的方法

    在Spark Streaming程序中,若需要使用有状态的流来统计一些累积性的指标,比如各个商品的PV.简单的代码描述如下,使用mapWithState()算子: val productPvStrea ...

  5. Spark Streaming之一:整体介绍

    提到Spark Streaming,我们不得不说一下BDAS(Berkeley Data Analytics Stack),这个伯克利大学提出的关于数据分析的软件栈.从它的视角来看,目前的大数据处理可 ...

  6. Spark Streaming源码解读之State管理之UpdataStateByKey和MapWithState解密

    本期内容 : UpdateStateByKey解密 MapWithState解密 Spark Streaming是实现State状态管理因素: 01. Spark Streaming是按照整个Bach ...

  7. 使用 Kafka 和 Spark Streaming 构建实时数据处理系统

    使用 Kafka 和 Spark Streaming 构建实时数据处理系统 来源:https://www.ibm.com/developerworks,这篇文章转载自微信里文章,正好解决了我项目中的技 ...

  8. Spark Streaming和Kafka集成深入浅出

    写在前面 本文主要介绍Spark Streaming基本概念.kafka集成.Offset管理 本文主要介绍Spark Streaming基本概念.kafka集成.Offset管理 一.概述 Spar ...

  9. 使用 Kafka 和 Spark Streaming 构建实时数据处理系统(转)

    原文链接:http://www.ibm.com/developerworks/cn/opensource/os-cn-spark-practice2/index.html?ca=drs-&ut ...

随机推荐

  1. Oracle中exists替代in语句

    大家都知道exists的速度要比in的速度快,也知道exists函数返回一个布尔值,也就是说exists函数里最后要是 a.id =b.id类似这种方式结束. 例如: SELECT * FROM TB ...

  2. C#中Timer定时器的使用示例

    关于C#中timer类 在C#里关于定时器类就有3个: 1.定义在System.Windows.Forms里 2.定义在System.Threading.Timer类里 3.定义在System.Tim ...

  3. BAT面试上机题从3亿个ip中找出访问次数最多的IP详解

    我们面临的问题有以下两点:1)数据量太大,无法在短时间内解决:2)内存不够,没办法装下那么多的数据.而对应的办法其实也就是分成1)针对时间,合适的算法+合适的数据结构来提高处理效率:2)针对空间,就是 ...

  4. rabbitmq消费端加入精确控频。

    控制频率之前用的是线程池的数量来控制,很难控制.因为做一键事情,做一万次,并不是每次消耗的时间都相同,所以很难推测出到底多少线程并发才刚好不超过指定的频率. 现在在框架中加入控频功能,即使开200线程 ...

  5. PHPExcel所遇到问题的知识点总结

    工作中进行excel的时候遇到了两个问题, 1.excel表中列值过大,由于没有进行特殊处理,程序没法正常运行: 2.列值中含有日期格式的文本,不能正确读取: 所以通过网络搜索,并解决了问题,记录一下 ...

  6. 1开放封闭原则OCP

    一.什么是开放封闭原则 开放封闭原则(Open-Closed Principle):一个软件实体 应当对扩展开放,则修改关闭. 在设计一个模块时,应当使得这个模块可以在不被修 改的前提下被扩展.也就是 ...

  7. Java对象序列化和反序列化的工具方法

    import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import ja ...

  8. c# 正则

    Regex reg = new Regex("^do(es)(xy)?$"); var result = reg.Match("doesxy"); foreac ...

  9. 自定义Flume Sink:ElasticSearch Sink

    Flume Sink的目的是从Flume Channel中获取数据然后输出到存储或者其他Flume Source中.Flume Agent启动的时候,它会为每一个Sink都启动一个SinkRunner ...

  10. python nose测试框架全面介绍十二 ----用例执行顺序打乱

    在实际执行自动化测试时,发现我们的用例在使用同一个资源的操作时,用例的执行顺序对测试结果有影响,在手工测试时是完全没法覆盖的. 但每一次都是按用例名字来执行,怎么打乱来执行的. 在网上看到一个有意思的 ...