一 UpdateStateByKey
UpdateStateByKey:统计全局的key的状态,但是就算没有数据输入,他也会在每一个批次的时候返回之前的key的状态。假设5s产生一个批次的数据,那么5s的时候就会更新一次的key的值,然后返回。
这样的缺点就是,如果数据量太大的话,而且我们需要checkpoint数据,这样会占用较大的存储。
如果要使用updateStateByKey,就需要设置一个checkpoint目录,开启checkpoint机制。因为key的state是在内存维护的,如果宕机,则重启之后之前维护的状态就没有了,所以要长期保存它的话需要启用checkpoint,以便恢复数据。
public classSparkUpdateKeyByState {
public staticvoid main(String[]args) {
SparkConf conf= new SparkConf().setAppName("SparkUpdate Key By State").setMaster("local[*]");
JavaStreamingContextjssc= new JavaStreamingContext(conf,Durations.seconds(2));
String checkpointDir= "hdfs://hdfs-cluster/ecommerce/checkpoint/state";
jssc.checkpoint(checkpointDir);
JavaReceiverInputDStream<String>messages = jssc.socketTextStream("hadoop-all-01",9999);
JavaDStream<String>words = messages.flatMap(newFlatMapFunction<String,String>() {
public Iterator<String>call(String s) throws Exception {
return Arrays.asList(s.split(" ")).iterator();
}
});
JavaPairDStream<String,Integer> pairs = words.mapToPair(newPairFunction<String,String, Integer>() {
public Tuple2<String,Integer> call(Stringword) throwsException {
return new Tuple2(word,1);
}
});
// 统计全局的word count,而不是单一的某一批次
JavaPairDStream<String,Integer>wordcounts = pairs.updateStateByKey
(new Function2<List<Integer>,Optional<Integer>,Optional<Integer>>() {
// 参数valueList:相当于这个batch,这个key新的值,可能有多个,比如(hadoop,1)(hadoop,1)传入的可能是(1,1)
// 参数oldState:就是指这个key之前的状态
public Optional<Integer>call(List<Integer>valueList, Optional<Integer>oldState) throwsException {
Integer newState= 0;
// 如果oldState之前已经存在,那么这个key可能之前液晶被统计过,否则说明这个key第一次出现
if (oldState.isPresent()) {
newState = oldState.get();
}
// 更新state
for (Integervalue : valueList) {
newState += value;
}
return Optional.of(newState);
}
});
jssc.start();
try {
jssc.awaitTermination();
} catch (InterruptedExceptione) {
e.printStackTrace();
}
jssc.close();
}
}
二 MapWithState
MapWithState:也是用于全局统计key的状态,但是它如果没有数据输入,便不会返回之前的key的状态,有一点增量的感觉。
这样做的好处是,我们可以只是关心那些已经发生的变化的key,对于没有数据输入,则不会返回那些没有变化的key的数据。这样的话,即使数据量很大,checkpoint也不会像updateStateByKey那样,占用太多的存储。
objectMapWithStateKafkaDirect extendsApp {
val conf = new SparkConf().setAppName("MapWithStateKafkaDirect").setMaster("local[*]")
val sc = SparkContext.getOrCreate(conf)
val checkpointDir= "hdfs://hdfs-cluster/ecommerce/checkpoint/2"
val brokers= "hadoop-all-01:9092,hadoop-all-02:9092,hadoop-all-03:9092"
def mappingFunction(key:String,value:Option[Int],state:State[Long]):(String,Long) = {
// 获取之前状态的值
val oldState= state.getOption().getOrElse(0L)
// 计算当前状态值
val newState= oldState + value.getOrElse(0)
// 更新状态值
state.update(newState)
// 返回结果
(key,newState)
}
val spec = StateSpec.function[String,Int,Long,(String,Long)](mappingFunction_)
def creatingFunc():StreamingContext= {
val ssc = new StreamingContext(sc,Seconds(5))
val kafkaParams= Map(
"metadata.broker.list"->brokers,
"group.id"->"MapWithStateKafkaDirect"
)
val topics= Set("count")
val messages= KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder](ssc,kafkaParams,topics)
val results:DStream[(String,Long)] =messages
.filter(_._2.nonEmpty)
.mapPartitions(iter =>{
iter.flatMap(_._2.split(" ").map((_,1)))
})
.mapWithState(spec)
results.print()
ssc.checkpoint(checkpointDir)
ssc
}
def close(ssc:StreamingContext,millis:Long):Unit = {
new Thread(newRunnable {
override def run(): Unit = {
// 当某个条件触发而关闭StreamingContext
Thread.sleep(millis)
println("满足条件,准备关闭StreamingContext")
ssc.stop(true,true)
println("成功关闭StreamingContext")
}
}).start()
}
val ssc = StreamingContext.getOrCreate(checkpointDir,creatingFunc)
ssc.start()
ssc.awaitTermination()
close(ssc,20000)
}