SparkStreaming是流式处理框架,是Spark API的扩展,支持可扩展、高吞吐量、容错的准实时数据流处理,它的数据来源可以是Kafka, Flume, Twitter, ZeroMQ或者TCP sockets处理后的数据可以存放在文件系统,数据库等,方便实时展现。
spark stream与storm一样都是流式处理框架,它们之间也有些区别:
- storm是纯实时处理框架,而spark stream是属于微批处理(个人认为是准实时),这也导致了spark stream的吞吐量比storm要高
- storm的事务机制(事务机制:对于一条数据,不多处理也不少处理,对于一条数据恰好处理一次,比如金融,股票等要求实时性比较高)要比spark stream更加完善
- Storm支持动态资源调度。(spark1.2开始和之后也支持)
SparkStreaming擅长复杂的业务处理,Storm不擅长复杂的业务处理,擅长简单的汇总型计算
目前spark stream用处广泛的原因:
1.秒级别延迟,通常应用程序是可以接受的,
2.可以应用机器学习,SparkSQL...可扩展性比较好,数据吞吐量较高
SparkStreaming运行流程
![440776afdfdff256cc9c03d6bf818792.png](https://img-blog.csdnimg.cn/img_convert/440776afdfdff256cc9c03d6bf818792.png)
sparkstream中会有个receivertask用来向服务器端口读取数据,receivertask是7*24小时一直在执行,一直接受数据将一段时间内接收来的数据保存到batch中,batchInterval(每批次数据处理间隔,可自定义,假设为5s),那么会将接收来的数据每隔5秒封装到一个batch中,这一个batch的数据又被封装到一个RDD中最终封装到一个DStream中。
每隔5秒通过SparkStreamin将得到一个DStream,在第6秒的时候计算这5秒的数据,假设执行任务的时间是3秒,那么第6~9秒一边在接收数据,一边在计算任务,9~10秒只是在接收数据。然后在第11秒的时候重复上面的操作。
注意:如果job执行的时间大于batchInterval如果接受过来的数据设置的级别是仅内存,接收来的数据会越堆积越多,最后可能会导致OOM(如果设置StorageLevel包含disk, 则内存存放不下的数据会溢写至disk, 加大延迟)
spark stream的API
SparkConf conf = new SparkConf().setMaster("local[2]").setAppName("WordCountOnline");
/**
* 在创建streaminContext的时候设置batch Interval
*/
JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));
JavaReceiverInputDStream<String>lines = jsc.socketTextStream("node5", 9999);
JavaDStream<String>words = lines.flatMap(new FlatMapFunction<String, String>() {
/**
*
*/
privatestaticfinallongserialVersionUID = 1L;
@Override
public Iterable<String> call(String s) {
return Arrays.asList(s.split(" "));
}
});
JavaPairDStream<String, Integer>ones = words.mapToPair(new PairFunction<String, String, Integer>() {
/**
*
*/
privatestaticfinallongserialVersionUID = 1L;
@Override
public Tuple2<String, Integer> call(String s) {
returnnew Tuple2<String, Integer>(s, 1);
}
});
JavaPairDStream<String, Integer>counts = ones.reduceByKey(newFunction2<Integer, Integer, Integer>() {
/**
*
*/
privatestaticfinallongserialVersionUID = 1L;
@Override
public Integer call(Integer i1, Integer i2) {
returni1 + i2;
}
});
//outputoperator类的算子
counts.print();
jsc.start();
//等待spark程序被终止
jsc.awaitTermination();
jsc.stop(false);
代码注意事项:
启动socket server 服务器:nc –lk 9999
receiver模式下接受数据,local的模拟线程必须大于等于2,一个线程用来receiver用来接受数据,另一个线程用来执行job。
Durations时间设置就是我们能接收的延迟度。这个需要根据集群的资源情况以及任务的执行情况来调节。
创建JavaStreamingContext有两种方式(SparkConf,SparkContext)
所有的代码逻辑完成后要有一个output operation类算子。
JavaStreamingContext.start() Streaming框架启动后不能再次添加业务逻辑。
JavaStreamingContext.stop() 无参的stop方法将SparkContext一同关闭,stop(false),不会关闭SparkContext。
JavaStreamingContext.stop()停止之后不能再调用start。
SparkStreaming+Kafka
1、receiver模式
![7f07b87b7ca2ef1580b04f5c8a676044.png](https://img-blog.csdnimg.cn/img_convert/7f07b87b7ca2ef1580b04f5c8a676044.png)
- 在SparkStreaming程序运行起来后,Executor中会有receiver tasks接收kafka推送过来的数据。数据会被持久化,默认级别为MEMORY_AND_DISK_SER_2,这个级别也可以修改。
- receiver task对接收过来的数据进行存储和备份,这个过程会有节点之间的数据传输。
- 备份完成后去zookeeper中更新消费偏移量
- 然后向Driver中的receiver tracker汇报数据的位置。
- 最后Driver根据数据本地化将task分发到不同节点上执行。
receiver模式中存在一个问题:当Driver进程挂掉后,Driver下的Executor都会被杀掉,当更新完zookeeper消费偏移量的时候,Driver如果挂掉了,就会存在找不到数据的问题,相当于丢失数据。
解决办法:开启WAL(write ahead log)预写日志机制,在接受过来数据备份到其他节点的时候,同时备份到HDFS上一份(我们需要将接收来的数据的持久化级别降级到MEMORY_AND_DISK),这样就能保证数据的安全性。
receiver模式的并行度:batch interval / block
2、Driect模式
- SparkStreaming+kafka 的Driect模式就是将kafka看成存数据的一方,不是被动接收数据,而是主动去取数据。
- 消费者偏移量也不是用zookeeper来管理,而是SparkStreaming内部对消费者偏移量自动来维护,默认消费偏移量是在内存中,当然如果设置了checkpoint目录,那么消费偏移量也会保存在checkpoint中。当然也可以实现用zookeeper来管理。
- Direct模式的并行度是由读取的kafka中topic的partition数决定的。
反压机制:spark.streaming.backpressure.enabled默认false
反压通常产生于这样的场景:短时负载高峰导致系统接收数据的速率远高于它处理数据的速率
实时流处理系统必须能够解决发送速率远大于系统能处理速率这个问题,大多数实时流处理系统采用反压机制解决这个问题
如何优雅的停止spark stream:
在代码中:spark.streaming.stopGracefullyOnShutdown 设置成true
在虚拟机中:kill -15/sigterm 进程号