流处理主要有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. 在interface vlan下敲no ip proxy-arp什么意思

    取消由路由带来的ARP请求. proxy ARP有哪些优点? 最主要的一个优点就是能够在不影响其他router的路由表的情况下在网络上添加一个新的router,这样使得子网的变化对主机是透明的 pro ...

  2. 简单探讨spring整合mybatis时sqlSession不需要释放关闭的问题

    https://blog.csdn.net/RicardoDing/article/details/79899686 近期,在使用spring和mybatis框架编写代码时,sqlSession不需要 ...

  3. 蜕变成蝶~Linux设备驱动之字符设备驱动

    一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流 ...

  4. 【中间件安全】IIS6安全加固规范

    1. 适用情况 适用于使用IIS6进行部署的Web网站. 2. 技能要求 熟悉IIS配置操作,能够利用IIS进行建站,并能针对站点使用IIS进行安全加固. 3. 前置条件 1. 根据站点开放端口.进程 ...

  5. php冒泡排序详解笔记

    冒泡 /* * 冒泡排序(从小到大) * 介绍: * 它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来. * 思路: * 比较相邻的元素.如果第一个比第二个大,就交换他 ...

  6. Factory——工厂方法

    一.定义 GOF上对工厂方法的意图如此描述:定义一个用于创建对象的接口,让子类决定实例化哪个类.Factory Method使一个类的实例化延迟到其子类. 作为类的开发者,我们通常会在类中提供构造器方 ...

  7. express中间件--Morgan 日志记录

    Morgan是一个node.js关于http请求的日志中间件 安装模块 npm install morgan --save #保存到package.json的依赖列表1使用方法 在终端打印日志...v ...

  8. Android学习:AlertDialog对话框

    AlertDialog可以生成各种内容的对话框,它生成的对话框包含4个区域:图标区,标题区,内容区,按钮区 <?xml version="1.0" encoding=&quo ...

  9. UCloud 的安全秘钥 (计蒜客初赛第五场)(待解决)

    20.7% 1200ms 262144K 每个 UCloud 用户会构造一个由数字序列组成的秘钥,用于对服务器进行各种操作.作为一家安全可信的云计算平台,秘钥的安全性至关重要.因此,UCloud 每年 ...

  10. react+mobx 编写 withStoreHistory 装饰器

    主要作用是向store里面注入一个history对象,方便story里面的函数调用 function withStoreHistory(storeName) { if (!storeName) ret ...