zoukankan      html  css  js  c++  java
  • Spark_总结七_troubleshooting

    转载标明出处 http://www.cnblogs.com/haozhengfei/p/07ef4bda071b1519f404f26503fcba44.html 


    Spark_总结七_troubleshooting

    1.yarn-client模式引起网卡流量激增问题?

       一个Driver和Executor中的task频繁进行通信,通信消息特别多,通信的频率特别高,运行完一个stage,接着运行下一个stage,又是频繁的通信。
       解决:yarn-cluster
     
        yarn-client模式,通常咱们就只会使用在测试环境中,你写好了某个spark作业,打了一个jar包,在某台测试机器上,用yarn-client模式去提交一下。因为测试的行为是偶尔为之的,不会长时间连续提交大量的spark作业去测试。还有一点好处,yarn-client模式提交,可以在本地机器观察到详细全面的log通过查看log,可以去解决线上报错的故障(troubleshooting)、对性能进行观察并进行性能调优。
        实际上线了以后,在生产环境中,都得用yarn-cluster模式,去提交你的spark作业。
    yarn-cluster模式,就跟你的本地机器引起的网卡流量激增的问题,就没有关系了。也就是说,
    就算有问题,也应该是yarn运维团队和基础运维团队之间的事情了。他们去考虑Yarn集群里面每台机器是虚拟机还是物理机呢?网卡流量激增后会不会对其他东西产生影响呢?如果网络流量激增,要不要给Yarn集群增加一些网络带宽等等这些东西。那就是他们俩个团队的事情了,和你就没有关系了
     
       大公司都是通过Yarn来进行调度,mapreduce on yarn、spark on yarn、甚至storm on yarn

    2.yarn-cluster 会报JVM栈内存溢出问题?

    问题描述一:
        yarn-client   PermGen 128M
        yarn-cluster PermGen  82M
       有的时候,运行一些包含了spark sql的spark作业,可能会碰到yarn-client模式下,可以正常提交运行;
    yarn-cluster模式下,可能是无法提交运行的,会报出JVM的PermGen(永久代)的内存溢出,OOM。
    PermGen(永久代)-->JVM里面的一个区域,就是会放Class里面一些字符串常量这些东西的。
     
       yarn-client模式下,driver是运行在本地机器上的,spark使用的JVM的PermGen的配置,
    是本地的spark-class文件(spark客户端是默认有配置的),JVM的永久代的大小是128M,
    这个是没有问题的;但是呢,在yarn-cluster模式下,driver是运行在yarn集群的某个节点上的,
    使用的是没有经过配置的默认设置(PermGen永久代大小),82M。
     
       spark-sql,它的内部是要进行很复杂的SQL的语义解析、语法树的转换等等,特别复杂,
    在这种复杂的情况下,如果说你的sql本身特别复杂的话,很可能会比较导致性能的消耗,内存的消耗。
    可能对PermGen永久代的占用会比较大。
     
       所以,此时,如果对永久代的占用需求,超过了82M的话,但是呢又在128M以内;就会出现如上所述的问题,
    yarn-client模式下,默认是128M,这个还能运行;如果在yarn-cluster模式下,默认是82M,就有问题了。
    会报出PermGen Out of Memory error log。
     
    问题解决:
            spark-submit提交任务的脚本中,加入以下配置即可:
            --conf spark.driver.extraJavaOptions="-XX:PermSize=128M -XX:MaxPermSize=256M"
     
    问题描述二:
       spark sql,sql,要注意,一个问题
                                     JVM Stack Memory Overflow,栈内存溢出
       sql,有大量的or语句。比如where keywords='' or keywords='' or keywords=''当达到or语句,有成百上千的时候,此时可能就会出现一个driver端的jvm stack overflow,JVM栈内存溢出的问题
       JVM栈内存溢出,基本上就是由于调用的方法层级过多,因为产生了大量的,非常深的,超出了JVM栈深度限制的,递归。递归方法。我们的猜测,spark sql,有大量or语句的时候,spark sql内部源码中,在解析sql,比如转换成语法树,或者进行执行计划的生成的时候,对or的处理是递归。or特别多的话,就会发生大量的递归。
     
    问题解决:
       这种时候,建议不要搞那么复杂的spark sql语句。采用替代方案:将一条sql语句,拆解成多条sql语句来执行。每条sql语句,就只有100个or子句以内;一条一条SQL语句来执行。根据生产环境经验的测试,一条sql语句,100个or子句以内,是还可以的。通常情况下,不会报那个栈内存溢出。
     

    3.序列化导致的报错?

    问题描述:
        用client模式去提交spark作业,观察本地打印出来的log。如果出现了类似于Serializable、Serialize等等字眼,报错的log,那么恭喜大家,就碰到了序列化问题导致的报错。
     
    问题解决:
     
       1、你的算子函数里面,如果使用到了外部的自定义类型的变量(executor中使用到了外部变量),那么此时,就要求你的自定义类型,必须是可序列化的。
     
       2、如果要将自定义的类型,作为RDD的元素类型,那么自定义的类型也必须是可以序列化的
    1. JavaPairRDD<Integer,Teacher> teacherRDD
    2. JavaPairRDD<Integer,Student> studentRDD
    3. studentRDD.join(teacherRDD)
    4. publicclassTeacherimplementsSerializable{
    5. }
    6. publicclassStudentimplementsSerializable{
    7. }
    序列化:
    1、executor中使用到了Driver端的变量(自定义对象)
    2、RDD<Person> Person
    3、RDD持久化的时候    _SER

    4.解决算子函数返回NULL导致问题

    问题描述:
        在算子函数中,返回null,有些算子函数里面,是需要我们有一个返回值的。但是,有时候,我们可能对某些值,就是不想有什么返回值。如果直接返回NULL的话,会报错的!!!
    1. return actionRDD.mapToPair(newPairFunction<Row,String,Row>(){
    2. privatestaticfinallong serialVersionUID =1L;
    3. @Override
    4. publicTuple2<String,Row> call(Row row)throwsException{
    5. returnnewTuple2<String,Row>("-666",RowFactory.createRow("-999"));
    6. returnnull
    7. }
    8. });
    问题解决:
       1、在返回的时候,返回一些特殊的值,不要返回null,比如“-999”
       2、在通过算子获取到了一个RDD之后,可以对这个RDD执行filter操作,进行数据过滤。
    filter内,可以对数据进行判定,如果是-999,那么就返回false,给过滤掉就可以了。
       3、大家不要忘了,之前咱们讲过的那个算子调优里面的coalesce算子,在filter之后,
    可以使用coalesce算子压缩一下RDD的partition的数量,让各个partition的数据比较紧凑一些。
    也能提升一些性能。

    5.YARN队列资源不足导致的Application直接失败

    问题描述:
       yarn 队列?队列资源:mem core
       如果说,你是基于yarn来提交spark。比如yarn-cluster或者yarn-client。你可以指定提交到哪个yarn队列上的,每个队列都是可以有自己的资源的。
       跟大家说一个生产环境中的,给spark用的yarn资源队列的情况:500G内存,200个cpu core。比如说,某个spark application,在spark-submit里面你自己配了,executor,80个;每个executor,4G内存;每个executor,2个cpu core。你的spark作业每次运行,大概要消耗掉320G内存,以及160个cpu core。乍看起来,咱们的队列资源,是足够的,500G内存,200个cpu core。
       首先,第一点,你的spark作业实际运行起来以后,耗费掉的资源量,可能是比你在spark-submit里面配置的,以及你预期的,是要大一些的。400G内存,190个cpu core。那么这个时候,的确,咱们的队列资源还是有一些剩余的。但是问题是,如果你同时又提交了一个spark作业上去,一模一样的。那就可能会出问题。第二个spark作业,又要申请320G内存+160个cpu core。结果,发现队列资源不足。。。。
       此时,可能会出现两种情况:(备注,具体出现哪种情况,跟你的YARN、Hadoop的版本,你们公司的一些运维参数,以及配置、硬件、资源肯能都有关系)
       1、YARN,发现资源不足时,你的spark作业,并没有等待在那里,等待资源的分配,而是直接打印一行fail的log,直接就fail掉了。
       2、YARN,发现资源不足,你的spark作业,就等待在那里。一直等待之前的spark作业执行完,等待有资源分配给自己来执行。
     
    问题解决:
       1、在你的J2EE(我们这个项目里面,spark作业的运行,之前说过了,J2EE平台触发的,执行spark-submit脚本),限制,同时只能提交一个spark作业到yarn上去执行确保一个spark作业的资源肯定是有的。
       2、你应该采用一些简单的调度区分的方式,比如说,你有的spark作业可能是要长时间运行的,
    比如运行30分钟;有的spark作业,可能是短时间运行的,可能就运行2分钟。此时,都提交到一个队列上去,肯定不合适。很可能出现30分钟的作业卡住后面一大堆2分钟的作业。分队列,可以申请(跟你们的YARN、Hadoop运维的同学申请)。你自己给自己搞两个调度队列。每个队列的根据你要执行的作业的情况来设置。在你的J2EE程序里面,要判断,如果是长时间运行的作业,就干脆都提交到某一个固定的队列里面去;如果是短时间运行的作业,就统一提交到另外一个队列里面去。这样,避免了长时间运行的作业,阻塞了短时间运行的作业。   
       3、你的队列里面,无论何时,只会有一个作业在里面运行。那么此时,就应该用我们之前讲过的性能调优的手段,去将每个队列能承载的最大的资源,分配给你的每一个spark作业,比如80个executor;6G的内存;3个cpu core。尽量让你的spark作业每一次运行,都达到最满的资源使用率,最快的速度,最好的性能;并行度,240个cpu core,720个task。
       4、在J2EE中,通过线程池的方式(一个线程池对应一个资源队列),来实现上述我们说的方案。在J2EE平台里面,怎么控制你的资源队列同时只能跑一个作业???可以用线程池来控制,创建线程池容量只有1的这么一个线程池,每一次提交一个作业,就会到这个线程池里面去,它空闲的时候就会有一个作业去跑,后面如果再有一个作业要跑的话,也扔到这个线程池里面,当然它的容量只有1,后面的这些作业线程要去执行,要去启动spark作业的线程,它就会在那里排队,这个线程池自动的给你实现了这个排队机制,不同的作业要放到不同的资源队列里面去运行,那就很简单嘛!不同的作业放到不同的线程池!你可以搞多个线程池,每个线程池就对应着一个资源队列!
    1. ExecutorService threadPool =Executors.newFixedThreadPool(1);
    2. threadPool.submit(newRunnable(){
    3. @Override
    4. publicvoid run(){
    5. }
    6. });
     
       spark如何提交到指定的资源队列中
     

    补充:

    1.yarn-client执行流程_Driver在整个Spark集群中的作用?

       1,在客户端给我们启动一个Driver
       2,去ResourceManager申请启动container
       3,通知一个NodeManager在container里面启动ApplicationMaster
       4,ApplicationMaster去找ResourceManager申请Executor
       5,ResourceManager返回可以启动的NodeManager的地址
       6,ApplicationMaster去找NodeManager启动Executor
       7,Executor进程会反过来去向Driver注册上去
       8,最后Driver接收到了Executor资源之后就可以去进行我们spark代码的执行了
       9,执行到某个action就触发一个Job
       10,DAGScheduler会划分JOB为一个个Stage
       11,TaskScheduler会划分Stage为一个个Task
       12,Task发送到Executor执行
       13,Driver就来进行Task的调度,并接受Executor中task执行的结果

    2.yarn-cluster执行流程

        1.在客户端提交我们执行任务的命令,这时客户端发送请求到ResourceManager,请求启动ApplicationMaster
        2.ResourceManager收到请求后,在某个NodeManager中分配Container,并启动ApplicationMaster(这个ApplicationMaster相当于Driver)
        3.ApplicationMaster发送请求到ResourceManager,请求一批Container,用于启动Executor
        4.Application得到ResourceManager的响应后,在NodeManager启动Executor,这里的NodeManager相当于Spark standalone模式下的Worker节点
        5.当Executor启动之后,会反向注册到Driver(ApplicationMaster)中
        6.接下来开始执行我们的代码,执行到某个action就触发一个Job
     
       1,spark-submit脚本提交spark application到ResourceManager
       2,去ResourceManager申请启动ApplicationMaster
       3,通知一个NodeManager去启动ApplicationMaster(Driver进程)
       4,ApplicationMaster去找ResourceManager申请Executor
       5,ResourceManager分配container,container代表你能启动的Executor占有的资源,包括内存+CPU
    返回已经启
       
       
       
     
    container的NodeManager的地址
       6,ApplicationMaster去找NodeManager在container里面申请启动Executor
       7,Executor进程会反过来去向Driver注册上去
       8,最后Driver接收到了Executor资源之后就可以去进行我们spark代码的执行了
       9,执行到某个action就触发一个JOB
       10,DAGScheduler会划分JOB为一个个Stage
       11,TaskScheduler会划分Stage为一个个Task
       12,Task发送到Executor执行
       13,Driver就来进行Task的调度
     
       到这里为止,ApplicationMaster(Driver),就知道自己有哪些资源可以用(executor)。
    然后就会去执行job、拆分stage、提交stage的task,进行task调度,分配到各个executor上面去执行。

    3.ApplicationMaster

       yarn中的核心概念,任何要在yarn上启动的作业类型(mr、spark),都必须有一个。
    每种计算框架(mr、spark),如果想要在yarn上执行自己的计算应用,那么就必须自己实现和
    提供一个ApplicationMaster
    相当于是实现了yarn提供的接口(spark自己开发的一个类)
       
    spark
       
       
    yarn-client模式下,application的注册(executor的申请)和计算task的调度,是分离开来的。
          standalone模式下,这两个操作都是Driver负责的。
       ApplicationMaster(ExecutorLauncher)负责executor的申请;Driver负责job和stage的划分,
    以及task的创建、分配和调度

    4.Yarn集群分成两种节点:

       ResourceManager    负责资源的调度
       NodeManager   负责资源的分配、应用程序执行这些东西

    5.Driver到底是什么?

       我们写的spark程序,打成jar包,用spark-submit来提交。jar包中的一个main类,通过jvm的命令启动起来。
    JVM进程,这个进程,其实就是咱们的Driver进程。Driver进程启动起来以后,执行我们自己写的main函数,从new SparkContext()。。。

    6.总结一下yarn-client和yarn-cluster模式的不同之处:

       yarn-client模式,driver运行在本地机器上的;
       yarn-cluster模式,driver是运行在yarn集群上某个nodemanager节点上面的。
     
       yarn-client会导致本地机器负责spark作业的调度,所以网卡流量会激增;
       yarn-cluster模式就没有这个问题。
     
       yarn-client的driver运行在本地,通常来说本地机器跟yarn集群都不会在一个机房的,性能可能不是特别好;
       yarn-cluster模式下,driver是跟yarn集群运行在一个机房内,性能上来说,也会好一些。
     
    ©All rights reserved
  • 相关阅读:
    创建索引
    列出所有的索引
    查看集群节点api
    集群健康检查api
    mapping 映射
    Elasticsearch 版本控制
    四种常见的 POST 提交数据方式
    HttpPost 传输Json数据并解析
    基本概念
    信用卡年轻消费群体数据分析和洞察报告
  • 原文地址:https://www.cnblogs.com/haozhengfei/p/07ef4bda071b1519f404f26503fcba44.html
Copyright © 2011-2022 走看看