Spark Streaming 与kafka的整合
模式一、SparkStreaming+kafka Receiver模式
SparkStreaming+kafka receiver模式处理数据采用了receiver接收器的模式,需要一个task一直处于占用接收数据,接收来的数据存储级别:MEMORY_AND_DISK_SER_2,这种模式几乎没有用的(1.6存在,2.3没有了)。
存在丢失数据问题:当接收完消息后,更新完zookeeper offset后,如果Driver挂掉,Driver下的Executor也会被kill,在executor内存中的数据多少会有丢失。
如何解决丢失数据问题:开启WAL(Write Ahead Log)预写日志机制。当Executor备份完成之后,向hdfs中也备份一份数据,备份完之后,再去更新消费者offset。如果开启WAL机制,可以将接收来的数据存储级别降级,降成MEMORY_AND_DISK_SER。实质上开启WAL机制也就是将每一批次数据存储到checkpoint目录中,所以开启WAL机制要设置checkpoint
开启之后带来了新问题:必须数据备份到HDFS完成之后,才会更新offset,紧接着会报数据位置,发送task处理数据,一方面会造成频繁的IO,另一方面会造成数据处理延迟加大
Receiver模式的并行度:【每一批次生成的DStream中的RDD的分区数】
spark.streaming.blockInterval=200ms。在batchInterval内每隔200ms,将接收来的数据封装到一个block中。batchInterval时间内生成的这些block组成了当前这个batch。假设batchInterval=5s,5s内生成的一个batch中有25个block。RDD->batch,RDD->partition,batch->block,这里每一个block就是对应RDD中的一个个partition。
如何提高RDD的并行度?当在batchInterval时间一定的情况下,减少spark.streaming.blockInterval值,建议这个值不要低于50ms,会造成Task多但是Task处理的数据量少。
SparkStreaming+kafka Receiver模式:
-
存在丢失数据问题
-
就算开启WAL机制解决了丢失数据问题,带来了新的问题,数据处理延迟加大
-
无法做到手动维护Consumer offset,采用zookeeper来维护offset
-
receiver模式底层消费kafka(0.8)采用的是高级Api实现 HighLevelConsumer api,不关心维护offset,无法从每批次中获取消费者offset和指定从某个offset继续消费数据
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import org.apache.spark.streaming.kafka.KafkaUtils;
import scala.Tuple2;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class SparkStreamingReceiver {
public static void main(String[] args) {
SparkConf sparkConf=new SparkConf();
sparkConf.setMaster("local[*]");
sparkConf.setAppName("sparkStreamingReceiver");
/**
* 开启预写日志WAL机制
* 必须开启checkpoint
*/
sparkConf.set("spark.streaming.receiver.writeAheadLog.enable","true");
JavaStreamingContext jsc = new JavaStreamingContext(sparkConf, Durations.milliseconds(5000));
jsc.checkpoint("./receivedata");
Map<String,Integer> topicConsumerConcurrency= new HashMap<String, Integer>();
/**
* 设置读取的topic和线程数
*/
topicConsumerConcurrency.put("t1123",1);