广播变量&累加变量

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<Person []> persons_br =  ctx.broadcast(persons);    	
    	Accumulator<Integer> count = ctx.accumulator(0);
    	List<String> data1 = new ArrayList<String>();
    	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<String> rdd1 = ctx.parallelize(data1, 2);
    	System.out.println(rdd1.glom().collect());
    	
    	rdd1.mapToPair(new PairFunction<String, String, Integer>() {
			@Override 
            public Tuple2<String, Integer> 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<String, Integer>(s, 1);  
            }
		}).collect();
    	System.out.println(count.value());
    	
    	rdd1.mapToPair(new PairFunction<String, String, Integer>() {
			@Override 
            public Tuple2<String, Integer> 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<String, Integer>(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<List<String>> broadcastList = null;
	/**
	 * 计数器!
	 */
	private static volatile Accumulator<Integer> 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<String> lines = jsc.socketTextStream("Master", 9999);

		/**
		 * 这里省去flatmap因为名单是一个个的!
		 */
		JavaPairDStream<String, Integer> pairs = lines.mapToPair(new PairFunction<String, String, Integer>() {
			@Override
			public Tuple2<String, Integer> call(String word) {
				return new Tuple2<String, Integer>(word, 1);
			}
		});
		JavaPairDStream<String, Integer> wordsCount = pairs.reduceByKey(new Function2<Integer, Integer, Integer>() {
			@Override
			public Integer call(Integer v1, Integer v2) {
				return v1 + v2;
			}
		});
		/**
		 * Funtion里面 前几个参数是 入参。 后面的出参。 体现在call方法里面!
		 *
		 */
//		wordsCount.foreach(new Function2<JavaPairRDD<String, Integer>,Time,boolean>() {
//			@Override
//			public boolean call(JavaPairRDD<String, Integer> rdd, Time time) throws Exception {
//				rdd.filter(new Function<Tuple2<String, Integer>, Boolean>() {
//					@Override
//					public Boolean call(Tuple2<String, Integer> 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();
	}
}

 

 

posted @ 2018-01-02 00:00  appointint  阅读(526)  评论(0编辑  收藏  举报