Spark整理(2)

Spark整理(2)

一,Standalone提交任务

1.1 Standalone-client提交任务
  • 提交命令
/spark-submit 
--master spark://node01:7077 
--class org.apache.spark.examples.SparkPi 
../lib/spark-examples-1.6.0-hadoop2.6.0.jar 
1000

或者

/spark-submit 
--master spark://node01:7077 
--deploy-mode client
--class org.apache.spark.examples.SparkPi 
../lib/spark-examples-1.6.0-hadoop2.6.0.jar 
1000
  • 执行原理图解
    在这里插入图片描述

  • 执行流程

1.集群启动后,worker节点会定期向Master汇报资源情况(cpu,内存等)

2.客户端提交程序后,会在客户端启动Driver进程

3.Driver会向Master申请当前程序需要的Application资源

4.资源申请成功后,Driver将task发送到Worker节点上执行

5.Driver将执行结果回收到Driver端。

  • 总结

client模式适用于测试调试程序。driver是在客户端启动的,这里的客户端就是指提交程序的节点。

在Driver端可以看到task执行的情况,生产环境不能使用client模式,是因为:

假设要提交100个Application到集群运行,Driver每次都会在提交程序的节点上启动,会导致客户端100次网卡流量暴增的问题。

1.2 Standalone-cluster提交任务
  • 提交命令
./spark-submit 
--master spark://node1:7077 
--deploy-mode cluster
--class org.apache.spark.examples.SparkPi 
../lib/spark-examples-1.6.0-hadoop2.6.0.jar 
100
  • 注意

Standalone-cluster提交方式,应用程序使用的所有的jar文件,必须保证所有的worker节点都要有,因为这种方式,Spark不会自动上传包。

解决方式:

1.将所有的依赖包和文件打到同一个包中,然后放在hdfs上。

2.将所有的依赖包和文件各放一份到worker节点

  • 执行原理图解
    在这里插入图片描述

  • 执行流程

1.首先是集群启动后,worker定期向master汇报资源情况

2.cluster模式下提交程序后,首先会去Master申请启动Driver进程

3.Master随机的在一台节点上开启Driver进程

4.Driver向Master申请Application程序运行所需资源

5.Master分配好资源后,Driver将Task发送到worker上执行

6.Driver回收任务执行结果,监控任务的状态

  • 总结

Driver进程是在集群某一个Worker上启动的,在客户端是无法查看task的运行情况的。因为集群提交Driver是随机分配在Worker节点上启动的,所以不存在网卡流量激增的问题。

  • Driver

1.向Master申请Application程序所需资源

2.分配task到Worker

3.回收task的运行结果

4.监控task的运行情况

二,Yarn模式提交任务

2.1 yarn-client提交任务
  • 提交命令
./spark-submit 
--master yarn
--class 
org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar
100

或者

./spark-submit 
--master yarn–client
--class 
org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar
100

或者

./spark-submit 
--master yarn 
--deploy-mode client 
--class 
org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar
100
  • 执行原理图解
    在这里插入图片描述

  • 执行流程

1.在客户端提交程序,会在当前节点启动一个Driver进程

2.Driver向RM申请启动AppMaster

3.RM随机在一个NM节点上启动AppMaster

4.AppMaster启动后,会向RM申请启动一批资源(Container),用于启动Exector

5.RM分配给AppMaster一批资源

6.AppMaster启动Exector

7.Driver发送task到Exector上执行,并监听task的运行情况和回收结果。

  • 总结

Yarn-client 模式同样是适用于测试,因为 Driver 运行在本地,Driver会与 yarn 集群中的 Executor 进行大量的通信,会造成客户机网卡流量的大量增加。

  • ApplicationMaster

1.为当前的Applicatin向RM申请启动资源

2.给NodeManager发送消息启动Exector

注意:ApplicationMaster 有 launchExecutor 和申请资源的功能,并没有作业调度的功能。

2.2 yarn-cluster提交任务方式
  • 提交方式
./spark-submit 
--master yarn 
--deploy-mode cluster
--class 
org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar
100

或者

./spark-submit 
--master yarn-cluster
--class 
org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar
100
  • 执行原理图解
    在这里插入图片描述

  • 执行流程

1.客户端提交程序到集群中,发送请求给RM请求启动AppMaster

2.RM随机在集群节点中分配一台启动AppMaster(相当于Driver)

3.AppMaster启动后,向RM申请执行给Application程序的所需资源

4.RM返回给AppMaster一批NodeManager资源(Container)

5.AppMaster发送消息到NodeManager,启动Exector,Exector启动后会反向注册给Driver

6.Driver分发task到Exector,并监听任务情况,回收任务结果

  • 总结

Yarn-Cluster 主要用于生产环境中,因为 Driver 运行在 Yarn 集群中某一台 nodeManager 中,每次提交任务的 Driver 所在的机器都是随机的,不会产生某一台机器网卡流量激增的现象,缺点是任务提交后不能看到日志。只能通过 yarn 查看日志。

  • ApplicationMaster的作用

1.为当前的Applicatin向RM申请启动资源

2.给NodeManager发送消息启动Exector

3.任务调度

三,补充算子

3.1 转换算子
  • Join LeftOutJoin RightOuterJoin FullOuterJoin

作用在<K,V>格式的RDD上。根据K进行连接,对(K,V)和(K,W)进行join返回(K,(V,W))

Join后的分区数和父RDD分区数多的那一个相同

java代码实现:

package com.shsxt.spark.java;


import com.google.common.base.Optional;
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.VoidFunction;
import scala.Tuple2;

import java.util.Arrays;

public class Ja_Join {

    public static void main(String args[]){

        SparkConf sparkConf = new SparkConf();
        sparkConf.setMaster("local").setAppName("join");

        JavaSparkContext sc = new JavaSparkContext(sparkConf);

        JavaPairRDD<Integer, String> nameRDD = sc.parallelizePairs(Arrays.asList(
                new Tuple2<Integer, String>(0, "aa"),
                new Tuple2<Integer, String>(1, "a"),
                new Tuple2<Integer, String>(2, "b"),
                new Tuple2<Integer, String>(3, "c")
        ));

        JavaPairRDD<Integer, Integer> scoreRDD = sc.parallelizePairs(Arrays.asList(
                new Tuple2<Integer, Integer>(1, 100),
                new Tuple2<Integer, Integer>(2, 200),
                new Tuple2<Integer, Integer>(3, 300),
                new Tuple2<Integer, Integer>(4, 400)
        ));

        JavaPairRDD<Integer, Tuple2<String, Integer>> joinRDD = nameRDD.join(scoreRDD);

        System.out.println(joinRDD.partitions().size() +" -----------  ");
        joinRDD.foreach(new VoidFunction<Tuple2<Integer, Tuple2<String, Integer>>>() {
            @Override
            public void call(Tuple2<Integer, Tuple2<String, Integer>> t) throws Exception {
                System.out.println(t);
            }
        });


        JavaPairRDD<Integer, Tuple2<String, Optional<Integer>>> leftJoin = nameRDD.leftOuterJoin(scoreRDD);

        leftJoin.foreach(new VoidFunction<Tuple2<Integer, Tuple2<String, Optional<Integer>>>>() {
            @Override
            public void call(Tuple2<Integer, Tuple2<String, Optional<Integer>>> rssult) throws Exception {
                System.out.println(rssult);
            }
        });

        JavaPairRDD<Integer, Tuple2<Optional<String>, Integer>> rightOuterJoin = nameRDD.rightOuterJoin(scoreRDD);

        rightOuterJoin.foreach(new VoidFunction<Tuple2<Integer, Tuple2<Optional<String>, Integer>>>() {
            @Override
            public void call(Tuple2<Integer, Tuple2<Optional<String>, Integer>> result) throws Exception {
                System.out.println(result);
            }
        });

        JavaPairRDD<Integer, Tuple2<Optional<String>, Optional<Integer>>> fullOuterJoin = nameRDD.fullOuterJoin(scoreRDD);

        fullOuterJoin.foreach(new VoidFunction<Tuple2<Integer, Tuple2<Optional<String>, Optional<Integer>>>>() {
            @Override
            public void call(Tuple2<Integer, Tuple2<Optional<String>, Optional<Integer>>> result) throws Exception {
                System.out.println(result);
            }
        });


    }
}
  • union

合并两个数据集,两个数据集的类型要一致

返回新的RDD的分区数是合并RDD分区数的总和

scala代码实现:

package com.shsxt.spark.scala

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}


object Sp_Union {

  def main(args: Array[String]): Unit = {

    val sparkConf = new SparkConf()
    sparkConf.setMaster("local").setAppName("union")

    val sc = new SparkContext(sparkConf)

    val RDD1 = sc.parallelize(Array(1,2,3),3)
    val RDD2 = sc.parallelize(Array(4,5,6),2)
    val result: RDD[Int] = RDD1.union(RDD2)

    println(result.partitions.length)  //输出 5

    result.foreach(println)
  }
}

java代码实现:

package com.shsxt.spark.java;


import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.rdd.RDD;
import org.apache.spark.sql.execution.columnar.INT;
import org.codehaus.janino.Java;

import java.util.Arrays;

public class Ja_Union {

    public static void main(String args[]){

        SparkConf sparkConf = new SparkConf();
        sparkConf.setMaster("local").setAppName("union");

        JavaSparkContext sparkContext = new JavaSparkContext(sparkConf);

        JavaRDD<Integer> rdd = sparkContext.parallelize(Arrays.asList(1, 2, 3),2);
        JavaRDD<Integer> rdd1 = sparkContext.parallelize(Arrays.asList(3, 6, 9),3);

        JavaRDD<Integer> result = rdd.union(rdd1);
        System.out.println(result.partitions().size());

        result.foreach(new VoidFunction<Integer>() {
            @Override
            public void call(Integer t) throws Exception {
                System.out.println(t);
            }
        });

    }
}
  • intersection

取两个数据集的交集

分区数取两个RDD中分区数较大的一个

scala代码实现:

package com.shsxt.spark.scala

import org.apache.spark.{SparkConf, SparkContext}


object Sp_Intersection {

  def main(args: Array[String]): Unit = {

    val sparkConf = new SparkConf()
    sparkConf.setMaster("local").setAppName("union")

    val sc = new SparkContext(sparkConf)

    val RDD1 = sc.parallelize(Array(1,2,3),3)
    val RDD2 = sc.parallelize(Array(3,5,6),2)

    val result = RDD1.intersection(RDD2)
    println(result.partitions.length)   //输出 3

    result.foreach(println)

  }

}
  • subtract

取两个数据集的差集

scala代码实现:

package com.shsxt.spark.scala

import org.apache.spark.{SparkConf, SparkContext}


object Sp_Subtract {

  def main(args: Array[String]): Unit = {

    val sparkConf = new SparkConf()
    sparkConf.setMaster("local").setAppName("union")

    val sc = new SparkContext(sparkConf)

    val RDD1 = sc.parallelize(Array(1,2,3),3)
    val RDD2 = sc.parallelize(Array(3,5,6),2)

    val result = RDD2.subtract(RDD1)  //前一个RDD的分区数

    println(result.partitions.length)

    result.foreach(println);

  }
}
  • mapPartition

与Map类似,遍历的单位是每个partition上的数据。

java代码:

package com.shsxt.spark.java;


import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;

import java.util.ArrayList;
import java.util.Iterator;

public class Ja_MapPartitions {

    public static void main(String args[]){

        SparkConf conf = new SparkConf();
        conf.setMaster("local").setAppName("mappartition");

        JavaSparkContext sc = new JavaSparkContext(conf);

        JavaRDD<String> lines = sc.textFile("./wc.txt",3);

        JavaRDD<String> mapPartitions = lines.mapPartitions(new FlatMapFunction<Iterator<String>, String>() {

            @Override
            public Iterable<String> call(Iterator<String> t) throws Exception {
                System.out.println("创建数据库连接......");
                ArrayList<String> list = new ArrayList<>();

                while (t.hasNext()) {
                    list.add(t.next());
                }
                System.out.println("批量插入数据........");
                System.out.println("关闭数据库连接......");

                return list;
            }
        });

        //触发算子 触发执行
        mapPartitions.collect();

        sc.stop();

    }
}
  • distinct(map+reduceByKey+map)

  • cogroup

当调用类型(K,V)和(K,W)的数据上时,返回一个数据集(K,(iterable,iterable))

java代码:

package com.shsxt.spark.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.VoidFunction;
import scala.Tuple2;

import java.util.Arrays;
import java.util.List;

public class Ja_Cogroup {

    public static void main(String args[]){

        SparkConf conf = new SparkConf().setAppName("ReduceOperator")
                .setMaster("local");
        JavaSparkContext sc = new JavaSparkContext(conf);

        List<Tuple2<String,String>> studentsList = Arrays.asList(
                new Tuple2<String,String>("1","zhangsan"),
                new Tuple2<String,String>("2","lisi"),
                new Tuple2<String,String>("2","wangwu"),
                new Tuple2<String,String>("3","maliu"));

        /**
         * 1 zhangsan 100
         * 1 zhangsan 100
         * 2 lisi 90
         * 2 lisi 60
         * 3 wagnwu 80
         * 3 wangwu 50
         */
        List<Tuple2<String,String>> scoreList = Arrays.asList(
                new Tuple2<String,String>("1","100"),
                new Tuple2<String,String>("2","90"),
                new Tuple2<String,String>("3","80"),
                new Tuple2<String,String>("1","1000"),
                new Tuple2<String,String>("2","60"),
                new Tuple2<String,String>("3","50"));

        JavaPairRDD<String, String> stuRDD = sc.parallelizePairs(studentsList);
        JavaPairRDD<String, String> scoreRDD = sc.parallelizePairs(scoreList);

        JavaPairRDD<String, Tuple2<Iterable<String>, Iterable<String>>> cogroupRDD = stuRDD.cogroup(scoreRDD);

        cogroupRDD.foreach(new VoidFunction<Tuple2<String, Tuple2<Iterable<String>, Iterable<String>>>>() {
            @Override
            public void call(Tuple2<String, Tuple2<Iterable<String>, Iterable<String>>> tuple2) throws Exception {
                System.out.println(tuple2);
            }
        });

        sc.close();

    }
}

  • mapPartitionWithIndex

遍历分区数据的同时加上分区号

java代码实现:

package com.shsxt.spark.java;


import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function2;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class Ja_MapPartitionWithIndex {

    public static void main(String args[]){

        SparkConf conf = new SparkConf();
        conf.setMaster("local").setAppName("mapPartitionWithIndex");
        JavaSparkContext sc = new JavaSparkContext(conf);

        List<String> list = Arrays.asList("zhangsan1", "zhangsan2", "lis1", "lisi2");

        //这里的第二个参数是设置并行度,也是RDD的分区数,并行度理论上来说设置大小为core的2-3倍
        JavaRDD<String> rdd = sc.parallelize(list, 3);

        JavaRDD<String> rdd1 = rdd.mapPartitionsWithIndex(new Function2<Integer, Iterator<String>, Iterator<String>>() {
            @Override
            public Iterator<String> call(Integer index, Iterator<String> t) throws Exception {
                ArrayList<String> list = new ArrayList<>();
                while (t.hasNext()) {
                    String s = t.next();
                    list.add(s);
                    System.out.println("partition id is "+index +",value is "+s );
                }

                return list.iterator();
            }
        }, true);

        rdd1.count();
        sc.stop();
    }
}
3.2 触发算子
  • foreachPartition

遍历的是每个partition的数据

java代码:

package com.shsxt.spark.java;


import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.VoidFunction;

import java.util.ArrayList;
import java.util.Iterator;

public class Ja_ForeachPartitions {

    public static void main(String args[]){

        SparkConf conf = new SparkConf();
        conf.setMaster("local").setAppName("mappartition");

        JavaSparkContext sc = new JavaSparkContext(conf);

        JavaRDD<String> lines = sc.textFile("./wc.txt",3);

        lines.foreachPartition(new VoidFunction<Iterator<String>>() {
            @Override
            public void call(Iterator<String> t) throws Exception {
                ArrayList<String> list = new ArrayList<>();
                System.out.println("打开数据库连接");
                while (t.hasNext()){
                    list.add(t.next());
                }
                System.out.println("批量插入数据");
                System.out.println("关闭连接.....");
            }
        });

        sc.close();

    }
}

四,相关术语解释

  • Master

资源管理的主节点(进程)

  • Cluster Manager

在集群上获取资源的外部服务(例如:standalone,Mesos,Yarn)

  • Worker Node(standalone)

资源管理的从节点(进程)或者说管理本机资源的进程

  • Application

基于Spark的用户程序,包含了Driver程序和运行在集群上的exector程序

  • Driver Program

用来连接工作进程(worker)的程序

  • Exector

是在一个Worker进程所管理的节点上为某Application启动的一个进程,该进程负责运行任务,并且负责将数据存在内存或者磁盘上。每个应用都有各自独立的的exectors.

  • Task

被送到某个Exector上的工作单元

  • Job

包含很多任务(task)的并行计算,可以看做和action对应

  • Stage

一个Job会被拆分很多组任务,每组任务被称为Stage(就像MapReduce分为MapTask和ReduceTask一样)

五,窄依赖和宽依赖

RDD之间有一系列的依赖关系,依赖关系又分为窄依赖和宽依赖

5.1 窄依赖
  • 父RDD和子RDD partition之间的关系是一对一
  • 父RDD和子RDD partition之间的关系是多对一
  • 不会有shuffle的产生
5.2 宽依赖
  • 父RDD和子RDD partition之间的关系是一对多
  • 会有shuffle的产生
5.3 窄宽依赖理解图

以WordCount为例
在这里插入图片描述

六,Stage

Spark任务会根据RDD之间的依赖关系,形成一个DAG有向无环图,DAG会提交给DAGScheduler,DAGSCHeduler会把DAG划分相互依赖的多个stage,划分stage的依据就是RDD之间的宽窄依赖。遇到宽依赖就划分stage,每个stage包含一个或多个Task任务。然后将这些task以task以taskSet的形式提交给TaskScheduler运行。

  • Stage是由一组并行的task组成

  • 切割规则,遇到宽依赖就切割stage

6.1 stage的计算模式

pipeline管道计算模式,pipeline只是一种计算思想,模式。
在这里插入图片描述

  • 数据一直在管道里面什么时候数据会落地?

1.对RDD进行持久化(cache,persist)

2.shuffle write的时候

  • Stage的task并行度是由stage的最后一个RDD的分区数决定的
  • 如何改变RDD的分区数(改变task的并行度)?

例如:reduceByKey(XXX,3), GroupByKey(4) 宽依赖一般都可以改变分区数。

6.2 pipeline计算模式的验证
package com.shsxt.spark.scala
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object Sp_Pipeline {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf()
    conf.setMaster("local").setAppName("pipeline")

    val sparkContext = new SparkContext(conf)

    val lines: RDD[String] = sparkContext.textFile("./wc.txt")

    val rdd2: RDD[String] = lines.map(x=> {
      println(x)
      x
    })

    val rdd3 = rdd2.filter(x=>{
      println(x+"===============")
      true
    })

    rdd3.collect()

    sparkContext.stop()

  }

}

执行结果:
在这里插入图片描述

可以看出,数据是一条一条处理

七,Spark资源调度和任务调度

  • 图例
    在这里插入图片描述

  • 流程描述

1.启动集群后,Worker节点会向Master节点汇报资源情况,Master掌握节点的资源情况。

2.当Spark提交一个Application后,根据RDD之间的依赖关系将Application形成一个DAG有向无环图。

3.任务提交后,Spark会在Driver端创建两个对象:DAGScheduler和TaskScheduler,DAGScheduler是任务调度的高层调度器,主要作用就是将DAG根据RDD之间的宽窄关系划分为一个个的Stage,然后将Stage以TaskSet的形式提交给TaskScheduler(TaskScheduler是任务调度的底层调度器,这里TaskSet其实就是一个集合,里面封装了一个个的task任务,也就是Stage的并行度task任务)

4.TaskScheduler会遍历TaskSet集合,拿到每个Task后将task发送到计算节点Exector中去执行,Task在Exector线程池中的运行情况会汇报给TaskScheduler.

5.当task执行失败时,则由TaskScheduler负责重试,将task重新发送给Executor去执行(不一定是上一个Exector)。默认重试三次,如果任然失败,那么这个task所在的Stage就失败了。

6.Stage失败了则由DAGScheduler负责重试,将TaskSet重新发送给TaskScheduler,Stage默认重试4次。如果任然失败,则认为Stage所在的Job失败,Job失败了,Application就失败了。

7.TaskScheduler不仅能重试失败的task,还会重试stragging(落后,缓慢)的task(执行速度比其他task慢太多的task)。TaskScheduler会启动一个新的task来和这个运行缓慢的task执行相同的处理逻辑。两个task哪个先执行完成,就以哪个task的执行结果为准。

  • 最后提到的就是Spark的推测执行机制。在Spark中推测执行机制默认是关闭的,可以通过spark.speculation属性来配置。

  • 图详解
    在这里插入图片描述

7.1 资源申请方式划分
  • 粗粒度资源申请(Spark)

Application执行之前,将所有的资源申请完毕,当资源申请成功后,才会进行任务的调度,当所有的task执行完成后,才会释放这部分资源

优点:

在Application执行之前,所有的资源都申请完毕,每一个task直接使用资源就可以了,不需要task在执行前自己去申请资源,task启动就快了,task执行快了,Stage执行就快了,Stage执行快了,Job执行就快了,Application执行也就快了。

缺点:

指到最后一个task执行完毕才回去释放资源,集群的资源无法充分利用

  • 细粒度资源申请(MapReduce)

Application执行前不申请资源,而是直接执行,让job中的每一个task在执行前自己去申请资源。task执行完毕后就返回资源

优点:

集群资源可以充分利用

缺点:

task自己去申请资源,task启动变慢,程序的执行运行变慢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值