SparkStreaming读取kafka数据(2)-DirectStream方式
前遍讲了Spark streaming处理kafka的数据的checkpoint机制方式
checkpoint的弊端
checkpoint的最大的弊端在于,一旦你的流式程序代码或配置改变了,或者更新迭代新功能了,这个时候,你先停旧的sparkstreaming程序,然后新的程序打包编译后执行运行,会发现两种情况: (1)启动报错,反序列化异常 (2)启动正常,但是运行的代码仍然是上一次的程序的代码。
为什么会出现上面的两种情况,这是因为checkpoint第一次持久化的时候会把整个相关的jar给序列化成一个二进制文件,每次重启都会从里面恢复,但是当你新的 程序打包之后序列化加载的仍然是旧的序列化文件,这就会导致报错或者依旧执行旧代码。
实际开发中程序经常变动,所以弊端很突出!!!
弊端的解决方法
spark官网给出了2种解决办法:
(1)旧的不停机,新的程序继续启动,两个程序并存一段时间消费。
(2)停机的时候,记录下最后一次的偏移量,然后新恢复的程序读取这个偏移量继续工作,从而达到不丢消息。
本篇采用方式(2):记录最新消费的offset值并更新到zookeeper,程序恢复时读取最新消费的offset值继续工作。
代码原理
spark streaming 的rdd可以被转换为HasOffsetRanges类型,进而得到所有partition的offset值
运行环境
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
<version>2.1.0</version>
</dependency>
kafka工具类
负责程序恢复时,读取最新消费的offset值和更新offset值到zookeeper
import kafka.common.TopicAndPartition;
import kafka.message.MessageAndMetadata;
import kafka.serializer.StringDecoder;
import org.apache.spark.SparkException;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.streaming.api.java.JavaInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import org.apache.spark.streaming.kafka.HasOffsetRanges;
import org.apache.spark.streaming.kafka.KafkaCluster;
import org.apache.spark.streaming.kafka.KafkaUtils;
import org.apache.spark.streaming.kafka.OffsetRange;
import scala.Tuple2;
import scala.collection.JavaConversions;
import scala.collection.mutable.ArrayBuffer;
import scala.util.Either;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* kafka offset 管理
*/
public class JavaKafkaManager implements Serializable {
private scala.collection.immutable.Map<String, String> kafkaParams;
private KafkaCluster kafkaCluster;
public JavaKafkaManager(Map<String, String> kafkaParams) {
this.kafkaParams = toScalaImmutableMap(kafkaParams);
kafkaCluster = new KafkaCluster(this.kafkaParams);
}
public JavaInputDStream<String> createDirectStream(
JavaStreamingContext jssc,
Map<String, String> kafkaParams,
Set<String> topics) throws SparkException {
String groupId = kafkaParams.get("group.id");
// 在zookeeper上读取offsets前先根据实际情况更新offsets
setOrUpdateOffsets(topics, groupId);
//从zookeeper上读取offset开始消费messa