SparkCore高级03

一、Spark on Yarn

将spark作业提交到yarn上执行,spark仅仅是作业的一个客户端。

1、在yarn上运行,根据官网

==>


spark-submit --class org.apache.spark.examples.SparkPi \

--master yarn \

/opt/app/spark-2.3.2/spark-2.3.2-bin-2.6.0-cdh5.7.0/examples/jars/spark-examples_2.11-2.3.2.jar \

3


2、报错修改

将HADOOP_CONF_DIR的路径放到环境变量

export HADOOP_CONF_DIR=/opt/app/hadoop-2.6.0-cdh5.7.0/etc/hadoop

成功执行!

3、结果

4、分析日志

这里的jar包是/opt/app/spark-2.3.2/spark-2.3.2-bin-2.6.0-cdh5.7.0/examples/jars/spark-examples_2.11-2.3.2.jar

du -sh

1)既然没配置,就配置

running-on-yarn页面:

2)spark和springboot一起使用,配成长服务,一直启动spark,就不会每次都要上传分发了。

5、./spark-submit on yarn还有参数可以用

--queue QUEUE_NAME--队列

--num-executors NUM---executor数量

--executor-cores NUM---executor-core的数量

6、spark shell启动yarn

./spark-shell --master yarn

7、on yarn执行作业

sc.textFile("/HBinzTest.txt").count

8、详细解析

jps

9、client vs cluster

driver运行在哪里,就是哪个模式

client:

cluster:

区别:

1)cluster模式的driver运行在集群里,driver与各节点的executor通信的网络会比client的好一些。

2)driver运行在client要时刻留意client的状态,client挂了,executor也就挂了。

3)cluster模式的日志在yarn上面,在客户端看不到。需要输入命令yarn logs -applicationId <app ID>,配置history。

总结:

yarn的参数:

二、Monitor

You can access this interface by simply opening http://<driver-node>:4040 in a web browser. If multiple SparkContexts are running on the same host, they will bind to successive ports beginning with 4040 (4041, 4042, etc).

Note that this information is only available for the duration of the application by default. To view the web UI after the fact, set spark.eventLog.enabled to true before starting the application. This configures Spark to log Spark events that encode the information displayed in the UI to persisted storage.

您可以访问该接口通过 http:// < driver-node >:4040 在一个webUI浏览器监控spark作业。 如果在同一主机上运行多个SparkContexts,他们将连续绑定到端口从4040开始(4041、4042、等)。 注意,此信息仅用于应用程序生命周期。 在应用程序开始运行前设置spark.eventLog.enabled

为true可以看历史的日志信息。

1、设置地址

/opt/app/spark-2.3.2/spark-2.3.2-bin-2.6.0-cdh5.7.0/conf/spark-defaults.conf

打开spark.eventLog.enabled true

2、历史记录地址:

修改地址:

3、启动的服务

1)启动服务

./sbin/start-history-server.sh

2)地址:http://<server-url>:18080 

4、配置spark.history.fs.logDirectory

配置两个,一个是看的spark.history.fs.logDirectory,一个是写入hdfs的spark.history.fs.logDirectory。

写入hdfs的spark.history.fs.logDirectory已经在spark-defaults.conf配置了。

而看的spark.history.fs.logDirectory配置逻辑如下:

1)分析

所有的spark.history.*参数都配置在SPARK_HISTORY_OPTS参数下面:

而SPARK_HISTORY_OPTS在spark-env.sh中配置

2)配置spark.history.fs.logDirectory和port

5、重新启动yarn和spark验证

spark/sbin目录下

./sbin/start-history-server.sh

1)监控/opt/app/spark-2.3.2/spark-2.3.2-bin-2.6.0-cdh5.7.0/logs/spark-HBinz-org.apache.spark.deploy.history.HistoryServer-1-hadoop002.out

tail -200f /opt/app/spark-2.3.2/spark-2.3.2-bin-2.6.0-cdh5.7.0/logs/spark-HBinz-org.apache.spark.deploy.history.HistoryServer-1-hadoop002.out

2)日志地址

地址:http://hadoop002:18080

3)跑一个本地任务

./spark-submit \

--class org.apache.spark.examples.SparkPi \

--master local[2] \

/opt/app/spark-2.3.2/spark-2.3.2-bin-2.6.0-cdh5.7.0/examples/jars/spark-examples_2.11-2.3.2.jar \

刷新:

点击APP ID看详细情况:

自己测试yarn的情况,一样的。

4)查看hdfs的日志文件

是JSON文件。

5)可以参数配置来定义hdfs日志刷新、清理等操作

6)日志压缩

github查源码

eventLog

搜compress

参数:

* spark.eventLog.compress - Whether to compress logged events

所以:多看源码!!!!

7)应用程序完成后,一定要执行sc.stop

6、其他一种监控方式    REST API

API接口:http://hadoop002:18080/api/v1/service_name

1)所有应有的情况

http://hadoop002:18080/api/v1/applications

2)具体某个job的情况

http://hadoop002:18080/api/v1/applications/local-1548504813730/jobs

三、coalesce vs repartition

1、coalesce

在当前类中寻找方法:Ctrl+Fn+F12

2、coalesce定义


/**
 * Return a new RDD that is reduced into `numPartitions` partitions.
 *
 * This results in a narrow dependency, e.g. if you go from 1000 partitions
 * to 100 partitions, there will not be a shuffle, instead each of the 100
 * new partitions will claim 10 of the current partitions. If a larger number
 * of partitions is requested, it will stay at the current number of partitions.
 *
 * However, if you're doing a drastic coalesce, e.g. to numPartitions = 1,
 * this may result in your computation taking place on fewer nodes than
 * you like (e.g. one node in the case of numPartitions = 1). To avoid this,
 * you can pass shuffle = true. This will add a shuffle step, but means the
 * current upstream partitions will be executed in parallel (per whatever
 * the current partitioning is).

总结:
结果会是一个窄依赖。 
如果你从1000个partition变成100个,不会有shuffle。  
如果请求分区大于原分区,则仍保持原来的分区数。  
然而,你可以通过设置第二个参数为true达到shuffle的作用。从而可以使得请求分区数大于原分区数。

3、案例:上传文件,查看partition数量

val data = sc.textFile("/data/HBinzTest.txt")

data.partition

data.partitions.size

拓展:

因为RDD不可变,所以data分区数还是2.需要赋值tranformation新的RDD

val RDD1 = data.coalesce(1)

RDD1.coalesce(1).partitions.size

1)coalesce(numpartitions,false)

将false改为true,可以让请求分区数大于元分区数

2)场景

生成小文件的情况下,可以通过coalesce把好几个小文件(散开的多余分区)合并。

2、repartition

调用了coalesce,只是默认参数shuffle为true。

总结:

200               200   1条      200          200      

rdd1 -map-> rdd2 -filter--coalesce-> rddc --> save...

coalesce多数情况是作用在filter之后,将分区压缩变小。

四、map vs mapPartitions

1、map

特点:作用在元素

案例:

package day03

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

import scala.collection.mutable.ListBuffer

object MapandMapPartitions {
  def main(args: Array[String]): Unit = {
    val sparkconf = new SparkConf().setAppName("MapandMapPartitions").setMaster("local[2]")
    val sc = new SparkContext(sparkconf)
//Todo...构建数据
    val employee = new ListBuffer[String]
    for (i <- 1 to 100){
      employee += "empolyee" +i
    }
    //目前employee是ListBuffer类型,要通过spark来操作保存到数据库需要转换成RDD
    val employeeRDD = sc.parallelize(employee)
    //将RDD保存到数据库的方法--map
    ruozeMap(employeeRDD)
    sc.stop()
  }
  def ruozeMap(employeeRDD:RDD[String])={
      employeeRDD.map(x =>{
      //对employeeRDD的每一个元素操作,每个元素拿到一个connection连接,数据库保存;map操作元素,每个元素都会去拿
connection,然后操作数据库
      val connection = DBUtils.getConnection()
      println(connection)
      //保存数据到mysql,这里不保存
        DBUtils.returnConnection(connection)
      }).foreach(println)
  }
}
结果:每个元素获取connection,打印一次

2、mapPartition

特点:

作用在partition,如果把所有的元素放在一个partition的话,进行数据库操作会很方便

案例:

def ruozeMapPartition(employeeRDD:RDD[String])= {
  println("employeeRDD.partitions.size:" + employeeRDD.partitions.size)

  employeeRDD.mapPartitions(partition=>{

    val connection = DBUtils.getConnection()
    DBUtils.returnConnection(connection)
    println(connection + "~~~~~~~~~~~~~~~~~~~~~")
    partition
  }).foreach(println)
结果:每个partition获取connection,打印一次,由于两个partition,所以打印2次,将分区的所有元素打印

五、foreach VS foreachPartition

1、foreach

案例:

package day03

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

import scala.collection.mutable.ListBuffer

object foreachandForeachPartitionApp {
  def main(args: Array[String]): Unit = {
    val sparkconf = new SparkConf().setAppName("MapandMapPartitions").setMaster("local[2]")
    val sc = new SparkContext(sparkconf)
    //Todo...构建数据
    val employee = new ListBuffer[String]
    for (i <- 1 to 100){
      employee += "empolyee:" + i
    }
    //目前employee是ListBuffer类型,要通过spark来操作保存到数据库需要转换成RDD
    val employeeRDD = sc.parallelize(employee)
    //将RDD保存到数据库的方法
    //ruozeforeach(employeeRDD)
    ruozeforeach(employeeRDD)
    sc.stop()
  }
def ruozeforeach(employeeRDD:RDD[String])={
    //这里用了foreach,所以后面就不需要再foreach
    employeeRDD.foreach(x =>{
      //拿到一个connection
      val connection = DBUtils.getConnection()
      println(connection + "~~~~~~~~~~~~~~~~")
      //保存数据到mysql
      DBUtils.returnConnection(connection)
    })
  }
}
package com.HBinz.spark.Spark.sql.day03

import scala.util.Random

object DBUtils {

  def getConnection()={
    new Random().nextInt(10)+""
  }
//归还connection,不实现
  def returnConnection(connection:String)={}
      ""
}

也是每个元素打印

2、foreachPartition

案例:

 def ruozeMapForeachPartition(employeeRDD:RDD[String])= {
    println("employeeRDD.partitions.size:" + employeeRDD.partitions.size)

    employeeRDD.foreachPartition(partition=>{

      val connection = DBUtils.getConnection()
      DBUtils.returnConnection(connection)
      println(connection + "~~~~~~~~~~~~~~~~~~~~~")
      partition
    })

  }

2个分区,就只拿两次connection,打印两次connection随机数。

总结:

只要设计到输出的或RDD操作数据库最好使用foreachPartition~!!!


思考:

1、数据量太大了,一个partition里面有一亿条数据,connection一直占用,可能会出现oom,怎么解决?

猜你喜欢

转载自blog.csdn.net/Binbinhb/article/details/88570007