一、前述

今天分享一篇SparkStreaming常用的算子transform和updateStateByKey。

  • 可以通过transform算子,对Dstream做RDD到RDD的任意操作。其实就是DStream的类型转换。

算子内,拿到的RDD算子外,代码是在Driver端执行的,每个batchInterval执行一次,可以做到动态改变广播变量。

  • 为SparkStreaming中每一个Key维护一份state状态,通过更新函数对该key的状态不断更新。

二、具体细节

        1、transform 是一个transformation类算子

package com.spark.sparkstreaming;

import java.util.ArrayList;
import java.util.List; import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext; import com.google.common.base.Optional; import scala.Tuple2;
/**
* 过滤黑名单
* transform操作
* DStream可以通过transform做RDD到RDD的任意操作。
* @author root
*
*/
public class TransformOperator {
public static void main(String[] args) {
SparkConf conf = new SparkConf();
conf.setMaster("local[2]").setAppName("transform");
JavaStreamingContext jsc = new JavaStreamingContext(conf,Durations.seconds(5)); //模拟黑名单
List<Tuple2<String,Boolean>> blackList = new ArrayList<Tuple2<String,Boolean>>();
blackList.add(new Tuple2<String,Boolean>("zhangsan",true));
//将黑名单转换成RDD
final JavaPairRDD<String, Boolean> blackNameRDD = jsc.sparkContext().parallelizePairs(blackList); //接受socket数据源
JavaReceiverInputDStream<String> nameList = jsc.socketTextStream("node5", 9999);
JavaPairDStream<String, String> pairNameList =
nameList.mapToPair(new PairFunction<String, String, String>() { /**
*这块代码在Driver端执行。
*/
private static final long serialVersionUID = 1L; @Override
public Tuple2<String, String> call(String s) throws Exception {
return new Tuple2<String, String>(s.split(" ")[1], s);
}
});
JavaDStream<String> transFormResult =
pairNameList.transform(new Function<JavaPairRDD<String,String>, JavaRDD<String>>() { /**
*
*/
private static final long serialVersionUID = 1L; @Override
public JavaRDD<String> call(JavaPairRDD<String, String> nameRDD)
throws Exception {
/**
* nameRDD:
* ("zhangsan","1 zhangsan")
* ("lisi","2 lisi")
* ("wangwu","3 wangwu")
* blackNameRDD:
* ("zhangsan",true)
*
* ("zhangsan",("1 zhangsan",[true]))
*
*/
JavaPairRDD<String, Tuple2<String, Optional<Boolean>>> leftOuterJoin =
nameRDD.leftOuterJoin(blackNameRDD);
//打印下leftOuterJoin
/*leftOuterJoin.foreach(new VoidFunction<Tuple2<String,Tuple2<String,Optional<Boolean>>>>() { *//**
*
*//*
private static final long serialVersionUID = 1L; @Override
public void call(Tuple2<String, Tuple2<String, Optional<Boolean>>> t)
throws Exception {
System.out.println(t);
}
});*/ //过滤:true的留下,false的过滤
//("zhangsan",("1 zhangsan",[true]))
JavaPairRDD<String, Tuple2<String, Optional<Boolean>>> filter =
leftOuterJoin.filter(new Function<Tuple2<String,Tuple2<String,Optional<Boolean>>>, Boolean>() { /**
*
*/
private static final long serialVersionUID = 1L; @Override
public Boolean call(Tuple2<String, Tuple2<String, Optional<Boolean>>> tuple)throws Exception {
if(tuple._2._2.isPresent()){
return !tuple._2._2.get();
}
return true;
}
}); JavaRDD<String> resultJavaRDD = filter.map(new Function<Tuple2<String,Tuple2<String,Optional<Boolean>>>, String>() { /**
*
*/
private static final long serialVersionUID = 1L; @Override
public String call(
Tuple2<String, Tuple2<String, Optional<Boolean>>> tuple)
throws Exception { return tuple._2._1;
}
}); //返回过滤好的结果
return resultJavaRDD;
}
}); transFormResult.print(); jsc.start();
jsc.awaitTermination();
jsc.stop();
}
}

 2、UpdateStateByKey算子(相当于对不同批次的累加和更新)

UpdateStateByKey的主要功能:
* 1、为Spark Streaming中每一个Key维护一份state状态,state类型可以是任意类型的, 可以是一个自定义的对象,那么更新函数也可以是自定义的。
* 2、通过更新函数对该key的状态不断更新,对于每个新的batch而言,Spark Streaming会在使用updateStateByKey的时候为已经存在的key进行state的状态更新

*  使用到updateStateByKey要开启checkpoint机制和功能。

*   多久会将内存中的数据写入到磁盘一份?

         如果batchInterval设置的时间小于10秒,那么10秒写入磁盘一份。如果batchInterval设置的时间大于10秒,那么就会batchInterval时间间隔写入磁盘一份。

 java代码:

package com.spark.sparkstreaming;

import java.util.Arrays;
import java.util.List; import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext; import com.google.common.base.Optional; import scala.Tuple2; /**
* UpdateStateByKey的主要功能:
* 1、为Spark Streaming中每一个Key维护一份state状态,state类型可以是任意类型的, 可以是一个自定义的对象,那么更新函数也可以是自定义的。
* 2、通过更新函数对该key的状态不断更新,对于每个新的batch而言,Spark Streaming会在使用updateStateByKey的时候为已经存在的key进行state的状态更新
*
* hello,3
* spark,2
*
* 如果要不断的更新每个key的state,就一定涉及到了状态的保存和容错,这个时候就需要开启checkpoint机制和功能
*
* 全面的广告点击分析
* @author root
*
* 有何用? 统计广告点击流量,统计这一天的车流量,统计点击量
*/ public class UpdateStateByKeyOperator {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setMaster("local[2]").setAppName("UpdateStateByKeyDemo");
JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));
/**
* 设置checkpoint目录
*
* 多久会将内存中的数据(每一个key所对应的状态)写入到磁盘上一份呢?
* 如果你的batch interval小于10s 那么10s会将内存中的数据写入到磁盘一份
* 如果bacth interval 大于10s,那么就以bacth interval为准

*
* 这样做是为了防止频繁的写HDFS
*/
JavaSparkContext sparkContext = jsc.sparkContext();
sparkContext.setCheckpointDir("./checkpoint"); // jsc.checkpoint("hdfs://node1:9000/spark/checkpoint");
// jsc.checkpoint("./checkpoint"); JavaReceiverInputDStream<String> lines = jsc.socketTextStream("node5", 9999); JavaDStream<String> words = lines.flatMap(new FlatMapFunction<String, String>() {
/**
*
*/
private static final long serialVersionUID = 1L; @Override
public Iterable<String> call(String s) {
return Arrays.asList(s.split(" "));
}
}); JavaPairDStream<String, Integer> ones = words.mapToPair(new PairFunction<String, String, Integer>() {
/**
*
*/
private static final long serialVersionUID = 1L; @Override
public Tuple2<String, Integer> call(String s) {
return new Tuple2<String, Integer>(s, 1);
}
}); JavaPairDStream<String, Integer> counts =
ones.updateStateByKey(new Function2<List<Integer>, Optional<Integer>, Optional<Integer>>() { /**
*
*/
private static final long serialVersionUID = 1L; @Override
public Optional<Integer> call(List<Integer> values, Optional<Integer> state) throws Exception {
/**
* values:经过分组最后 这个key所对应的value [1,1,1,1,1]
* state:这个key在本次之前之前的状态
*/
Integer updateValue = 0 ;
if(state.isPresent()){
updateValue = state.get();
} for (Integer value : values) {
updateValue += value;
}
return Optional.of(updateValue);
}
});
//output operator  counts.print(); jsc.start(); jsc.awaitTermination(); jsc.close(); } }

 scala代码:

package com.bjsxt.sparkstreaming

import org.apache.spark.SparkConf
import org.apache.spark.streaming.Durations
import org.apache.spark.streaming.StreamingContext object Operator_UpdateStateByKey {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local[2]").setAppName("updateStateByKey")
val jsc = new StreamingContext(conf,Durations.seconds(5))
//设置日志级别
jsc.sparkContext.setLogLevel("WARN")
//设置checkpoint路径
jsc.checkpoint("hdfs://node1:9000/spark/checkpoint") val lineDStream = jsc.socketTextStream("node5", 9999)
val wordDStream = lineDStream.flatMap { _.split(" ") }
val pairDStream = wordDStream.map { (_,1)} val result = pairDStream.updateStateByKey((seq:Seq[Int],option:Option[Int])=>{
var value = 0
value += option.getOrElse(0)
for(elem <- seq){
value +=elem
} Option(value)
}) result.print()
jsc.start()
jsc.awaitTermination()
jsc.stop()
}
}

结果:

 可见从启动以来一直维护这个累加状态!!!

 2、windows窗口函数(实现一阶段内的累加 ,而不是程序启动时)

  假设每隔5s 1个batch,上图中窗口长度为15s,窗口滑动间隔10s。

        窗口长度和滑动间隔必须是batchInterval的整数倍。如果不是整数倍会检测报错。

优化后的window操作要保存状态所以要设置checkpoint路径,没有优化的window操作可以不设置checkpoint路径。

package com.spark.sparkstreaming;

import java.util.Arrays;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext; import scala.Tuple2; /**
* 基于滑动窗口的热点搜索词实时统计
* @author root
*
*/
public class WindowOperator { public static void main(String[] args) {
SparkConf conf = new SparkConf()
.setMaster("local[2]")
.setAppName("WindowHotWord"); JavaStreamingContext jssc = new JavaStreamingContext(conf, Durations.seconds(5));
/**
* 设置日志级别为WARN
*
*/
jssc.sparkContext().setLogLevel("WARN");
/**
* 注意:
* 没有优化的窗口函数可以不设置checkpoint目录
* 优化的窗口函数必须设置checkpoint目录
*/
// jssc.checkpoint("hdfs://node1:9000/spark/checkpoint");
jssc.checkpoint("./checkpoint");
JavaReceiverInputDStream<String> searchLogsDStream = jssc.socketTextStream("node04", 9999);
//word 1
JavaDStream<String> searchWordsDStream = searchLogsDStream.flatMap(new FlatMapFunction<String, String>() { /**
*
*/
private static final long serialVersionUID = 1L; @Override
public Iterable<String> call(String t) throws Exception {
return Arrays.asList(t.split(" "));
}
}); // 将搜索词映射为(searchWord, 1)的tuple格式
JavaPairDStream<String, Integer> searchWordPairDStream = searchWordsDStream.mapToPair( new PairFunction<String, String, Integer>() { private static final long serialVersionUID = 1L; @Override
public Tuple2<String, Integer> call(String searchWord)
throws Exception {
return new Tuple2<String, Integer>(searchWord, 1);
} });
/**
* 每隔10秒,计算最近60秒内的数据,那么这个窗口大小就是60秒,里面有12个rdd,在没有计算之前,这些rdd是不会进行计算的。
* 那么在计算的时候会将这12个rdd聚合起来,然后一起执行reduceByKeyAndWindow操作 ,
* reduceByKeyAndWindow是针对窗口操作的而不是针对DStream操作的。
*/
JavaPairDStream<String, Integer> searchWordCountsDStream = searchWordPairDStream.reduceByKeyAndWindow(new Function2<Integer, Integer, Integer>() { private static final long serialVersionUID = 1L; @Override
public Integer call(Integer v1, Integer v2) throws Exception {
return v1 + v2;
}
}, Durations.seconds(15), Durations.seconds(5)); //窗口长度,滑动间隔 /**
* window窗口操作优化:不用设置checkpoint目录。
*/
// JavaPairDStream<String, Integer> searchWordCountsDStream =
//
// searchWordPairDStream.reduceByKeyAndWindow(new Function2<Integer, Integer, Integer>() {
//
// private static final long serialVersionUID = 1L;
//
// @Override
// public Integer call(Integer v1, Integer v2) throws Exception {
// return v1 + v2;
// }
//
// },new Function2<Integer, Integer, Integer>() {
//
// private static final long serialVersionUID = 1L;
//
// @Override
// public Integer call(Integer v1, Integer v2) throws Exception {
// return v1 - v2;
// }
//
// }, Durations.seconds(15), Durations.seconds(5)); searchWordCountsDStream.print(); jssc.start();
jssc.awaitTermination();
jssc.close();
} }

Scala代码:

package com.bjsxt.sparkstreaming

import org.apache.spark.SparkConf
import org.apache.spark.streaming.Durations
import org.apache.spark.streaming.StreamingContext object Operator_Window {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local[2]").setAppName("updateStateByKey")
val jsc = new StreamingContext(conf,Durations.seconds(5))
//设置日志级别
jsc.sparkContext.setLogLevel("WARN")
//设置checkpoint路径
jsc.checkpoint("hdfs://node1:9000/spark/checkpoint")
val lineDStream = jsc.socketTextStream("node04", 9999)
val wordDStream = lineDStream.flatMap { _.split(" ") }
val mapDStream = wordDStream.map { (_,1)} //window没有优化后的
val result = mapDStream.reduceByKeyAndWindow((v1:Int,v2:Int)=>{
v1+v2
}, Durations.seconds(60), Durations.seconds(10)) //优化后的
// val result = mapDStream.reduceByKeyAndWindow((v1:Int,v2:Int)=>{
// v1+v2
// }, (v1:Int,v2:Int)=>{
// v1-v2
// }, Durations.seconds(60), Durations.seconds(10)) result.print()
jsc.start()
jsc.awaitTermination()
jsc.stop()
}
}

结果:

【Spark篇】---SparkStreaming算子操作transform和updateStateByKey的更多相关文章

  1. SparkStreaming算子操作,Output操作

    SparkStreaming练习之StreamingTest,UpdateStateByKey,WindowOperator 一.SparkStreaming算子操作 1.1 foreachRDD 1 ...

  2. 【SparkStreaming学习之二】 SparkStreaming算子操作

    环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk1.8 scala-2.10.4(依赖jdk1.8) spark ...

  3. 【Spark篇】---Spark中控制算子

    一.前述 Spark中控制算子也是懒执行的,需要Action算子触发才能执行,主要是为了对数据进行缓存. 控制算子有三种,cache,persist,checkpoint,以上算子都可以将RDD持久化 ...

  4. Spark-读写HBase,SparkStreaming操作,Spark的HBase相关操作

    Spark-读写HBase,SparkStreaming操作,Spark的HBase相关操作 1.sparkstreaming实时写入Hbase(saveAsNewAPIHadoopDataset方法 ...

  5. Spark Streaming中的操作函数讲解

    Spark Streaming中的操作函数讲解 根据根据Spark官方文档中的描述,在Spark Streaming应用中,一个DStream对象可以调用多种操作,主要分为以下几类 Transform ...

  6. Spark Streaming通过JDBC操作数据库

    本文记录了学习使用Spark Streaming通过JDBC操作数据库的过程,源数据从Kafka中读取. Kafka从0.10版本提供了一种新的消费者API,和0.8不同,因此Spark Stream ...

  7. 【Spark篇】---Spark调优之代码调优,数据本地化调优,内存调优,SparkShuffle调优,Executor的堆外内存调优

    一.前述 Spark中调优大致分为以下几种 ,代码调优,数据本地化,内存调优,SparkShuffle调优,调节Executor的堆外内存. 二.具体    1.代码调优 1.避免创建重复的RDD,尽 ...

  8. 【转帖】HBase读写的几种方式(二)spark篇

    HBase读写的几种方式(二)spark篇 https://www.cnblogs.com/swordfall/p/10517177.html 分类: HBase undefined 1. HBase ...

  9. sparkRDD:第3节 RDD常用的算子操作

    4.      RDD编程API 4.1 RDD的算子分类 Transformation(转换):根据数据集创建一个新的数据集,计算后返回一个新RDD:例如:一个rdd进行map操作后生了一个新的rd ...

随机推荐

  1. nand flash和nor flash的区别

    NOR和NAND是现在市场上两种主要的非易失闪存技术. Intel于1988年首先开发出NOR flash技术,彻底改变了原先由EPROM和EEPROM一统天下的局面. 东芝于1989年开发出NAND ...

  2. 利用Google趋势来预测比特币价格 - 续1

    之前发布了一篇文章利用Google趋势来预测比特币价格,说到了看到一篇文章很朴素的介绍了google趋势和比特币价格的一个关系.觉得很简单直白,就根据那个模型写了个程序,部署起来了,从十一月十四号到现 ...

  3. 表关联ID相同数据update修改

    UPDATE 表1 e,表2 c SET e.被修改字段='修改值为..' WHERE e.id=c.id

  4. redis.Redis与redis.StrictRedis区别

    redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令(比如,SET命令对应与StrictRedi ...

  5. ElasticSearch本人启动报错总结

    1.报错关键代码如下: Exception in thread "main" 2019-04-28 03:53:04,339 main ERROR No log4j2 config ...

  6. Linux-网络基础

    1.如何将俩台不同网络中的电脑通信,见下图

  7. Luogu P3381 (模板题) 最小费用最大流

    <题目链接> 题目大意: 给定一张图,给定条边的容量和单位流量费用,并且给定源点和汇点.问你从源点到汇点的最带流和在流量最大的情况下的最小费用. 解题分析: 最小费用最大流果题. 下面的是 ...

  8. C - Thief in a Shop - dp完全背包-FFT生成函数

    C - Thief in a Shop 思路 :严格的控制好k的这个数量,这就是个裸完全背包问题.(复杂度最极端会到1e9) 他们随意原来随意组合的方案,与他们都减去 最小的 一个 a[ i ] 组合 ...

  9. 【安全性测试】drozer中关于AttackSurface的一些理解

    在推荐扫描Android APP的工具中,扫描组件可以推荐drozer.使用过drozer的使用者知道,如何查找各个组件上的攻击层面 run app.package.AttackSurface . 它 ...

  10. oracle之序列用法

    序列用于生成唯一.连续序号的对象序列是可以升序.降序的使用create sequence语句创建序列SQL>CREATE SEQUENCE stu_seq    START WITH 1     ...