1、 广播&累加器

我们传递给Spark的函数,如map(),或者filter()的判断条件函数,能够利用定义在函数之外的变量,但是集群中的每一个task都会得到变量的一个副本,并且task在对变量进行的更新不会被返回给driver。而Spark的两种共享变量:累加器(accumulator)和广播变量(broadcast variable),在广播和结果聚合这两种常见类型的通信模式上放宽了这种限制。 使用累加器可以很简便地对各个worker返回给driver的值进行聚合。

由于对于worker节点来说,累加器的值是不可访问的,所有对于worker上的task,累加器是write-only的。这使得累加器可以被更高效的实现,而不需要在每次更新时都进行通信。

Spark保证:在终止操作中对累加器的操作只执行一次,而转化操作中则可能多次执行。

累加器的操作可以多种,比如算术加法 MAX,只要这些操作符合交换律和结合律。

累加器会多次传递给Executor节点(而广播只一次)。

1. Accumulator

import java.util.ArrayList;
import java.util.List;
import org.apache.spark.Accumulator;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.broadcast.Broadcast;
import scala.Tuple2;
public class AccumulatorDemo {
public static void main(String[] xx){
SparkConf conf = new SparkConf();
conf.setMaster("local[4]");
conf.setAppName("WordCounter");
conf.set("spark.default.parallelism", "4");
conf.set("spark.testing.memory", "2147480000");
JavaSparkContext ctx = new JavaSparkContext(conf);
Person[] persons = new Person[10000];
Broadcast persons_br = ctx.broadcast(persons);
Accumulator count = ctx.accumulator(0);
List data1 = new ArrayList();
data1.add("Cake");
data1.add("Bread");
data1.add("");
data1.add("Cheese");
data1.add("Milk");
data1.add("Toast");
data1.add("Bread");
data1.add("");
data1.add("Egg");
data1.add("");
JavaRDD rdd1 = ctx.parallelize(data1, 2);
System.out.println(rdd1.glom().collect());
rdd1.mapToPair(new PairFunction() {
@Override
public Tuple2 call(String s) throws Exception {
long id = Thread.currentThread().getId();
System.out.println("s:" + s + " in thread:" + id);
if(s.equals("")){
count.add(1);
}
return new Tuple2(s, 1);
}
}).collect();
System.out.println(count.value());
rdd1.mapToPair(new PairFunction() {
@Override
public Tuple2 call(String s) throws Exception {
long id = Thread.currentThread().getId();
System.out.println("s:" + s + " in thread:" + id);
if(s.equals("")){
count.add(1);
// System.out.println("c:"+count.value());
}
System.out.println(persons_br.value().length);
return new Tuple2(s, 1);
}
}).collect();
System.out.println(count.value());
ctx.stop();
}
}
class Person{}
2.BroadCast
import java.util.Arrays;
import java.util.List;
import org.apache.spark.Accumulator;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.broadcast.Broadcast;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.Time;
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;
/**
* 实例:利用广播进行黑名单过滤! 检查新的数据 根据是否在广播变量-黑名单内,从而实现过滤数据。
*/
public class BroadCastDemo {
/**
* 创建一个List的广播变量
*
*/
private static volatile Broadcast> broadcastList = null;
/**
* 计数器!
*/
private static volatile Accumulator accumulator = null;
public static void main(String[] args) {
SparkConf conf = new SparkConf().setMaster("local[2]").setAppName("WordCountOnlineBroadcast");
conf.set("spark.testing.memory", "2147480000");
JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));
/**
* 注意:分发广播需要一个action操作触发。 注意:广播的是Arrays的asList
* 而非对象的引用。广播Array数组的对象引用会出错。 使用broadcast广播黑名单到每个Executor中!
*/
broadcastList = jsc.sparkContext().broadcast(Arrays.asList("Hadoop", "Mahout", "Hive"));
/**
* 累加器作为全局计数器!用于统计在线过滤了多少个黑名单! 在这里实例化。
*/
accumulator = jsc.sparkContext().accumulator(0, "OnlineBlackListCounter");
JavaReceiverInputDStream lines = jsc.socketTextStream("Master", 9999);
/**
* 这里省去flatmap因为名单是一个个的!
*/
JavaPairDStream pairs = lines.mapToPair(new PairFunction() {
@Override
public Tuple2 call(String word) {
return new Tuple2(word, 1);
}
});
JavaPairDStream wordsCount = pairs.reduceByKey(new Function2() {
@Override
public Integer call(Integer v1, Integer v2) {
return v1 + v2;
}
});
/**
* Funtion里面 前几个参数是 入参。 后面的出参。 体现在call方法里面!
*
*/
//wordsCount.foreach(new Function2,Time,boolean>() {
//@Override
//public boolean call(JavaPairRDD rdd, Time time) throws Exception {
//rdd.filter(new Function, Boolean>() {
//@Override
//public Boolean call(Tuple2 wordPair) throws Exception {
//if (broadcastList.value().contains(wordPair._1)) {
///**
// * accumulator不仅仅用来计数。 可以同时写进数据库或者缓存中。
// */
//accumulator.add(wordPair._2);
//return false;
//} else {
//return true;
//}
//};
///**
// * 广播和计数器的执行,需要进行一个action操作!
// */
//}).collect();
//System.out.println("广播器里面的值" + broadcastList.value());
//System.out.println("计时器里面的值" + accumulator.value());
//return null;
//}
//});
jsc.start();
try {
jsc.awaitTermination();
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
jsc.close();
}
}