Spark学习-Streaming

Spark Streaming

  • Spark Streaming
    • 对比Storm
    • Spark Streaming 处理逻辑
    • 对比 SparkCore / SparkSQL
  • Code

Spark Streaming

流式处理框架,7*24h 不间断运行

对比Storm

Storm处理流式数据是:来一条,出一条。
是纯实时处理。

Spark Streaming 处理逻辑

会等待一段时间,可以认为设置等待多久。
比如等待10s,那么等到10s后,会把这期间的所有数据批量处理,然后输出结果。
简而言之:来一段时间内的数据,一起处理。

Storm Spark Streaming
处理流式数据特点 纯实时的。1 => 1 会等待一段时间,然后统一处理。是准实时处理
特点 擅长处理汇总型业务。 吞吐量高,相对而言处理快。擅长处理复杂的业务(因为批处理的原因)。支持SparkSQL, SparkCore
总结 微批处理
另外 可以设置等待时间很短,那么可以达到Storm的效果

对比 SparkCore / SparkSQL

Spark Streaming 处理的是 DStream, 类比SparkSQL的DF。
SparkStreaming中有 Transformation / outputOperator 算子,类比于SparkCore的 Transformation / Action算子。
Spark学习-Streaming_第1张图片具体描述:

  • SparkStreaming是微批处理数据,7*24h不间断运行
  • SparkStreaming处理数据时,首先启动一个job,这个job使用一个task来一直接收数据。task将一段时间内接收到的数据封装到一个batch中。
  • batch没有分布式计算特性,被封装到一个RDD中,这个RDD又被封装到一个DStream中
  • 生成DStream之后,SparkStreaming启动job处理这个DStream。
  • SS 底层操作的就是 DStream。 DStream有自己的Transformation类算子,lazy执行,需要 outputOperator类算子出发执行。

关于一段时间的细节:
假设batchInterval = 5s, SS 启动后,(假设SS处理这一个批次数据的时间是3s),
0-5s:一直接收数据;
5-8s:一边接收、一边处理数据;
8-10s:只接收数据;
10-13s: 接收+处理
13-15s:只接收

可看出,由于后面的集群5s内有2s是休息的,这个集群不能被充分利用。不好。
同理,当假设SS处理的时间超过5s,那么 最终接收来的数据是放到内存中会有OOM (out of memeory) 风险。如果接收的数据放到内存和磁盘里,会加大延迟。

Code

import org.apache.spark.SparkConf;
import org.apache.spark.SparkContext;
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.broadcast.Broadcast;
import org.apache.spark.streaming.Durations;
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;
import java.util.Iterator;

/**
 * @author 
 * @since 6/21/20 10:14 AM
 */
public class SparkStreamingTest {
    public static void main(String[] args) throws InterruptedException {
        SparkConf conf = new SparkConf().setMaster("local[2]").setAppName("SparkStreamingTest");
        // 此处写local 等价于 local[1],是模拟 单线程. local[2] 指定2个线程。一个线程用于接收数据,另一个用于处理生成的DStream
        JavaSparkContext sc = new JavaSparkContext(conf);
        sc.setLogLevel("WARN");
        JavaStreamingContext jsc = new JavaStreamingContext(sc, Durations.seconds(5));

        /*
        设置5s之后,
        从此处开始,
          接受5s内的数据,一次处理
         */

        JavaReceiverInputDStream socketTextStream = jsc.socketTextStream("192.168.101.130", 9999);
        // 在指定的ip机器上,$ nc -lk 9999

        JavaDStream words = socketTextStream.flatMap(new FlatMapFunction() {
            // flatMap: one to many

            private static final long serialVersionUID = 1L;

            @Override
            public Iterator call(String lines) throws Exception {
                return Arrays.asList(lines.split(" ")).iterator();
            }
        });

        JavaPairDStream pairWords = words.mapToPair(new PairFunction() {

            private static final long serialVersionUID = 1L;

            @Override
            public Tuple2 call(String word) throws Exception {
                return new Tuple2(word, 1);
            }
        });

        // k-v 格式的DStream,内部是 k-v格式的RDD
        JavaPairDStream reduceByKeyDStream = pairWords.reduceByKey(new Function2() {
            private static final long serialVersionUID = 1L;

            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1 + v2;
            }
        });

        // outputOperator to trigger. e.g. print(), foreachRDD()
//        reduceByKey.print(10); // 某一个 outputOp算子

        /*
         * 从DStream 中拿到 RDD/pairRDD,对RDD进行操作transformation/action。如果不action的化,Dsream也不会触发。
         * 注意:只有对rdd 进行 action时, reduceByKeyDStream.foreachRDD才会真正触发。
         * 广播:可以动态改变广播变量。通过读取外部文件,修改外部文件。
         */
        reduceByKeyDStream.foreachRDD(new VoidFunction>() { // foreachRDD 也是触发类算子
            private static final long serialVersionUID = 1L;

            // get each RDD

            @Override
            public void call(JavaPairRDD pairRDD) throws Exception {
                // 注意:这里的RDD 执行的地方不同于 上面DStream的在executor里,
                // 此处rdd是在 Driver里执行
                System.out.println("Driver ....................,每5s打印一次。总是要打印的。无论有没有数据过来。有Driver的地方有 sparkContext");
                System.out.println("Driver side 可以广播东西");
                SparkContext context = pairRDD.context(); // 必须从 pairRDD处拿到 context,不能直接用main里定义的。
                JavaSparkContext sc = new JavaSparkContext(context);
                Broadcast hello = sc.broadcast("hello"); // 此处还可以读取外部的文件。当外部文件更新时,5s后也可以读取到新的文件。 实现:动态改变广播变量
                String value = hello.value();
                System.out.println(value);

                // transform pairRDD to another pairRDD
                JavaPairRDD newPairRDD = pairRDD.mapToPair(new PairFunction, String, Integer>() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public Tuple2 call(Tuple2 tuple2) throws Exception {
                        System.out.println("Executor....................只有有数据过来处理时,才打印");
                        return new Tuple2<>(tuple2._1 + "~", tuple2._2);
                    }
                });

                // sout
                newPairRDD.foreach(new VoidFunction>() {
                    @Override
                    public void call(Tuple2 tuple2) throws Exception {
                        System.out.println("rdd.foreach ......................., only when data comes");
                        System.out.println(tuple2);
                    }
                });
            }
        });

        jsc.start(); // 因为SS是7*24h, 需要启动
        jsc.awaitTermination(); // 等待被终结
//        jsc.stop(); // 这个stop 是调用不到的 ??? 可以停止

        /*
        一直循环这段代码
         */

    }
}

在指定的ip机器上:

$ nc -lk 9999
之后,在cli界面 手动输入一些字符

附:
关于 nc -lk 9999 什么意思?
nc is netcat, 9999 is port

nc
-l 开启 监听模式,用于指定nc将处于监听模式。通常 这样代表着为一个 服务等待客户端来链接指定的端口。
-p<通信端口> 设置本地主机使用的通信端口。有可能会关闭
-k<通信端口>强制 nc 待命链接.当客户端从服务端断开连接后,过一段时间服务端也会停止监听。 但通过选项 -k 我们可以强制服务器保持连接并继续监听端口。

你可能感兴趣的:(AI,/,BigData,/,Cloud)