SprkStream流式处理

简介

  • SparkStream流式处理框架 ,与Storm相比之下 ,Storm处理的数据是一条条处理的更为实时 . SparkStream是微批处理 , 对数据进行一小批一小批的处理 . 它是 Spark API 的扩展,SparkStream底层也是RDD , 支持可扩展、高吞吐量、容错的准实时数据流处理 , 它处理数据也需要时间, 例如5秒 , 很短 , 但严格意义上也不是完全实时的 .
  • sparkStream和Storm的区别:
    • SparkStream的吞吐量要比Storm高.
    • Storm的事务机制要比sparkStream要完善 .
    • Storm支持动态资源调度 , sparkStream1.2之后也开始支持.
    • 复杂的业务处理场景适合用SparkStream , Storm适合建的汇总型计算 .
  • 实时数据的来源可以是:Kafka, Flume, Twitter, ZeroMQ 或者 TCP sockets,并且可以使用高级功能的复杂算子来处理流数据。例如:map,reduce,join,window 。最终,处理后的数据可以
    存放在文件系统,数据库等,方便实时展现。
  • SparkStream操作的是DSream , 底层是RDD .
  • SparkStream的处理流程:
    在这里插入图片描述
    • client端发送数据
    • reciver task 7*24 小时一直在执行,一直接受数据 , reciver task会独占一个线程 , 分配好资源确保够用 .
    • 数据保存在batch中 , 可以通过设置batchInterval ,来设定多久让数据封装到batch中
    • batch会被封装成RDD
    • RDD又会被封装成DStream
    • DStream可以被转换算子对RDD进行处理 , 例如map , flatmap …
    • DStream最后会被output operator处理后变成我们想要的结果 , output operrator功能和触发算子一样 .
    • socket Server是指kafka这样的数据源.
    • 设想 ,batchInterval为5秒 , 计算处理这批数据的时间为3秒 , 我们在0-5秒的时候封装完一批数据 , 在5-10秒的时间里一边将这批数据进行处理完成 , 同时接受着另外一批数据 , 这样的情况是较为理想的 .
      可是如果batchInterval为5秒 , 计算处理这批数据的时间可能大于5秒 , 我们在0-5秒的时候封装完一批数据 , 在没能在5-10秒的时候将这批数据处理完 , 同时又会有一批新数据被封装完, 等待计算处理 . 这样的情况下 , 数据就会产生堆积 , 而且随着时间的流失 , 数据堆积会越来越严重 , 如果sparkStream的数据储存方式采用的是数据存储化级别中的memory_only (只保存内存) 的策略的话 , 最终可能会导致OOM (默认是memory_only,内存不够会保存磁盘) . 其实就是保证处理数据的时间短于接收数据的时间即可 .

SparkStream API

  • 数据源:socket TCP
package com.java;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.StreamingContext;
import org.apache.spark.streaming.api.java.JavaDStream;
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;

import java.util.Arrays;

/**
 * Created by Administrator on 2019/2/22.
 */
public class SparkStreamTest {
        public static void main(String[] args){
            // local[2]: reciver task会独占一个线程 , 所以分配2个 , 一个接受一个处理 
            SparkConf sparkConf = new SparkConf().setMaster("local[2]").setAppName("sparkStream");
            // Java的sparmStream操作对象 , 每5秒读取一次数据
            JavaStreamingContext javaStreamingContext = new JavaStreamingContext(sparkConf, Durations.seconds(5));
            // 数据来源是指定服务器节点通过SocketTCP的方式和9999端口来访问
            JavaReceiverInputDStream<String> dStream = javaStreamingContext.socketTextStream("192.168.200.212", 9999);

            // 处理数据 wordcount
            // 1. 切分单词
            JavaDStream<String> stringJavaDStream = dStream.flatMap(new FlatMapFunction<String, String>() {
                @Override
                public Iterable<String> call(String s) throws Exception {
                    return Arrays.asList(s.split(" "));
                }
            });
            // 2 . map <word , 1>
            JavaPairDStream<String, Integer> stringIntegerJavaPairDStream = stringJavaDStream.mapToPair(new PairFunction<String, String, Integer>() {
                @Override
                public Tuple2<String, Integer> call(String s) throws Exception {
                    return new Tuple2<>(s, 1);
                }
            });
            // 3 . 聚合
            JavaPairDStream<String, Integer> reduceDStream = stringIntegerJavaPairDStream.reduceByKey(new Function2<Integer, Integer, Integer>() {
                @Override
                public Integer call(Integer integer, Integer integer2) throws Exception {
                    return integer + integer2;
                }
            });

            // 4. 输出
            reduceDStream.print();
            // 启动流
            javaStreamingContext.start();
            // 让stream流阻塞 ,等待命令关闭 
            javaStreamingContext.awaitTermination();
            // 如果上一步收到命令 , 流关闭
            javaStreamingContext.stop();
        }
}

- 1 . 注意指定的节点要安装nc : `yum install nc -y`
- 2 . 通过命令:`nc -lk 9999`访问端口 , 之后可以通过命令行手写数据 . (注意服务器要关闭防火墙)
  • 数据源:kafka
    在这里插入图片描述
    (spark.streaming.blockInterval官方建议最好不要低于50毫秒)
    在这里插入图片描述

  • SparkStream的算子

    • foreachRDD 算子:

      • foreachRDD是DStream中output operator类(触发)的算子
      • foreachRDD可以遍历得到DStream中的RDD , 可以在这个算子内对RDD使用RDD的Transformation类算子进行转化 ,但是一定要使用RDD的Action类算子触发执行
      • foreachRDD可以得到DStream中的RDD , 在这个算子内 , RDD算子外执行的代码是在river端执行 , RDD算子内的代码是在Executor中执行.
    • transFormResult算子:

      • transFormResult和foreachRDD的区别是transFormResult返回的是RDD
    • updateStateByKey算子

      • 和reduceByKey相似, 将RDD的Value值聚合成List , 还有一个参数Optional 是从开始到上一次遍历的所有累加值
      • list的值累加是本次的和 , list的累加值再累加Optional , 则是从开始到结束所有值的累加(相当于自带了一个计数器)
      • 运行updateStateByKey算子 , 最好配合JavaStreamingContext.checkpoint(Path) . 实时流式处理一般会一直运行下去 , 如果程序出现问题中断 , 重启的时候就可以接着上一次的断点继续处理.
      • 如果bacth interval小于10秒 , checkpoint每10秒将内存数据写入磁盘 ,如果大于10秒 , checkpoint写入时间间隔和bacth interval等同.
package com.java;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.StreamingContext;
import org.apache.spark.streaming.api.java.JavaDStream;
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;

import java.util.Arrays;

/**
 * Created by Administrator on 2019/2/22.
 */
public class SparkStreamTest {
        public static void main(String[] args){
            // local[2]: reciver task会独占一个线程 , 所以分配2个 , 一个接受一个处理
            SparkConf sparkConf = new SparkConf().setMaster("local[2]").setAppName("sparkStream");
            // Java的sparmStream操作对象 , 每5秒读取一次数据
            JavaStreamingContext javaStreamingContext = new JavaStreamingContext(sparkConf, Durations.seconds(5));
            // 数据来源是指定服务器节点通过SocketTCP的方式和9999端口来访问
            JavaReceiverInputDStream<String> dStream = javaStreamingContext.socketTextStream("192.168.200.212", 9999);

            // 处理数据 wordcount
            // 1. 切分单词
            JavaDStream<String> stringJavaDStream = dStream.flatMap(new FlatMapFunction<String, String>() {
                @Override
                public Iterable<String> call(String s) throws Exception {
                    return Arrays.asList(s.split(" "));
                }
            });
            // 2 . map <word , 1>
            JavaPairDStream<String, Integer> stringIntegerJavaPairDStream = stringJavaDStream.mapToPair(new PairFunction<String, String, Integer>() {
                @Override
                public Tuple2<String, Integer> call(String s) throws Exception {
                    return new Tuple2<>(s, 1);
                }
            });
            // 3 . 聚合
            JavaPairDStream<String, Integer> reduceDStream = stringIntegerJavaPairDStream.reduceByKey(new Function2<Integer, Integer, Integer>() {
                @Override
                public Integer call(Integer integer, Integer integer2) throws Exception {
                    return integer + integer2;
                }
            });

            /**
             * foreachRDD 算子:
             *    foreachRDD是DStream中output operator类(触发)的算子
             *    foreachRDD可以遍历得到DStream中的RDD , 可以在这个算子内对RDD使用RDD的Transformation类算子进行转化 ,但是一定要使用RDD的Action类算子触发执行
             *    foreachRDD可以得到DStream中的RDD , 在这个算子内 , RDD算子外执行的代码是在river端执行 , RDD算子内的代码是在Executor中执行.
             * transFormResult算子:
             *     transFormResult和foreachRDD的区别是transFormResult返回的是RDD
             * updateStateByKey算子
             *      和reduceByKey相似, 将RDD的Value值聚合成List , 还有一个参数Optional 是从开始到上一次遍历的所有累加值
             *      list的值累加是本次的和 , list的累加值再累加Optional , 则是从开始到结束所有值的累加(相当于自带了一个计数器)
             *      运行updateStateByKey算子  , 最好配合JavaStreamingContext.checkpoint(Path) . 实时流式处理一般会一直运行下去 , 如果程序出现问题中断 , 重启的时候就可以接着上一次的断点继续处理.
             *          如果bacth interval小于10秒 , checkpoint每10秒将内存数据写入磁盘 ,如果大于10秒 , checkpoint写入时间间隔和bacth interval等同.
             */
            // Sream流 - foreachRDD : 将DStream转成RDD ,
            reduceDStream.foreachRDD(new VoidFunction<JavaPairRDD<String, Integer>>() {
                @Override
                public void call(JavaPairRDD<String, Integer> pairRDD) throws Exception {
                    // 得到RDD之后 , 输出RDD的内容
                    pairRDD.foreach( x -> System.out.println(x) );  // lambda表达式
                }
            });
            // 启动流
            javaStreamingContext.start();
            // 让stream流等待
            javaStreamingContext.awaitTermination();
            // 处理完之后关闭 , stop()会将sparkContext一同关闭. stop(false)只会关闭StreamContext , sparkContext不会关闭 .
            javaStreamingContext.stop(false);
            JavaSparkContext javaSparkContext = javaStreamingContext.sparkContext();
        }
}

  • 窗口函数
    每隔10秒 , 计算最近30秒的数据
    在这里插入图片描述
package com.java;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.function.*;
import org.apache.spark.streaming.Durations;
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;
import scala.actors.threadpool.Arrays;

/**
 * Created by Administrator on 2019/2/22.
 */
public class SparkStreamWindowTest {
        public static void main(String[] args){
            SparkConf sparkConf = new SparkConf().setMaster("local[2]").setAppName("SparkStreamWindow");
            JavaStreamingContext javaStreamingContext = new JavaStreamingContext(sparkConf, Durations.seconds(10));
            JavaReceiverInputDStream<String> stringJavaReceiverInputDStream = javaStreamingContext.socketTextStream("192.168.200.212", 9999);

            JavaPairDStream<String, Integer> dStream = stringJavaReceiverInputDStream.flatMap(new FlatMapFunction<String, String>() {
                @Override
                public Iterable<String> call(String s) throws Exception {
                    return Arrays.asList(s.split(" "));
                }
            }).mapToPair(new PairFunction<String, String, Integer>() {
                @Override
                public Tuple2<String, Integer> call(String s) throws Exception {
                    return new Tuple2<>(s, 1);
                }
            });

            //使用开窗函数对每10秒对前30秒的数据进行处理
            JavaPairDStream<String, Integer> windowDStream = dStream.reduceByKeyAndWindow(new Function2<Integer, Integer, Integer>() {
                @Override
                public Integer call(Integer integer, Integer integer2) throws Exception {
                    return integer + integer2;
                }
            }, Durations.seconds(30), Durations.seconds(10));//处理30秒的数据 , 每10秒处理一次
            
           windowDStream.print();

            javaStreamingContext.start();
            javaStreamingContext.awaitTermination();
            javaStreamingContext.stop();


        }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值