1. Spark项目的创建
说明一点,这里创建的项目,比之前稍微复杂一点点–基于maven的聚合和继承项目。
创建父工程,再创建多个子模块,这里用到spark-core
因为我不想再重新截图,所以用之前的,模块名会不完全相同,但都是一个意思,但能看懂
注意:父模块,一般不做开发,也就可以删除src相关目录;主要的作用就是用来管理所有的子模块,管理整个项目中使用到的依赖及其版本。
创建子目录–通用common
2. 项目编码
1. 添加模块为scala项目
第一步,在src下面创建一个scala目录,用于编写scala代码,并且将scala的目录指定为source root
发现无法直接创建scala的class
修改project structure,添加scala模块到core中
给core模块添加
2. 本地编码运行
需要注意的是,入口类为SparkContext。
不同版本有不同的SparkContext,java版本的是JavaSparkContext,scala的版本就是SparkContext;SparkSQL的入口有SessionContext;SparkStreaming的入口又是StreamingContext。
加载本地数据"C:\Users\70201\Desktop\wordcount.txt"
加载hdfs数据"hdfs://hadoop003:9000/data/spark/hello.txt"
master: 代表的spark作业运行的方式,常见的运行方式有local模式、standalone模式、yarn模式
local模式的写法: spark作业在本地来进行运行,没有提交到集群中去
local :为当前spark作业分配一个工作线程
local[N] :为当前spark作业分配N个工作线程,但是不能超过电脑可用个工作线程
local[*] :为当前spark作业分配可用个工作线程
local[R, N] :为当前spark作业分配N个工作线程,如果提交作业失败,会进行最多R次的重试
standalone模式的写法
普通的分布式:spark://<ip>:<port>
ha : spark://<ip1>:<port1>,<ip2>:<port2>
yarn模式的写法
master=yarn
注意:
standalone式和yarn模式要通常配合deploy-mode来进行使用
deploy-mode意为作业运行的方式,有两个选项:
client :默认值,driver或者sparkContext的创建与运行实在作业提交的机器上面完成
cluster :driver的创建和运行,与executor的创建和运行都是在集群中来完成,也就是都在worker上面来完成创建初始化并运行
通常,生产的deploy-mode用cluster,测试和开发用client。
object ScalaWordCount {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[*]").setAppName(ScalaWordCount.getClass.getSimpleName)
val sparkContext = new SparkContext(conf)
val rdd:RDD[String] = sparkContext.textFile("C:\\Users\\70201\\Desktop\\wordcount.txt")
rdd.flatMap(_.split("\\s+")).map((_,1)).reduceByKey(_+_).foreach({case(word,count)=>println(word+"---"+count)})
sparkContext.stop()
}
}
3. spark日志的管理
全局管理
就是项目classpath下面引入log4j.properties配置文件进行管理
# 基本日志输出级别为INFO,输出目的地为console
log4j.rootCategory=INFO, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
# 输出配置的是spark提供的webui的日志级别
log4j.logger.org.spark_project.jetty=INFO
log4j.logger.org.spark_project.jetty.util.component.AbstractLifeCycle=ERROR
log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=INFO
log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=INFO
log4j.logger.org.apache.parquet=ERROR
log4j.logger.parquet=ERROR
局部管理
就是在当前类中进行日志的管理。
import org.apache.log4j.{Level, Logger}
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.apache.hadoop").setLevel(Level.WARN)
Logger.getLogger("org.spark_project").setLevel(Level.WARN)
4.打包spark程序
打包程序在下面
如果不需要第三方依赖的打包的话,直接使用idea打包:
打包完成
5.提交到虚拟机本地
spark程序提交到集群,就应该是通过spark-submit脚本来完成的。
spark-submit-wc-local.sh脚本
#!/bin/sh
SPARK_HOME=/opt/module/spark-2.4.3
${SPARK_HOME}/bin/spark-submit \
--class com.offcn.bigdata.spark.RemoteScalaWordCountApp \
--master local \
--deploy-mode client \
/root/spark/wc.jar \
hdfs://hadoop003:9000/test/input/wordcount.txt
打包程序
package com.offcn.bigdata.spark
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* @author ljh
* @create 2020-12-06 18:17
*/
object RemoteScalaWordCountApp {
def main(args: Array[String]): Unit = {
if(args == null || args.length != 1) {
println(
"""
|Usage: <input>
|""".stripMargin)
System.exit(-1)
}
val Array(input) = args
val conf = new SparkConf()
.setAppName(s"${RemoteScalaWordCountApp.getClass.getSimpleName}")
val sc = new SparkContext(conf)
val lines: RDD[String] = sc.textFile(input)//加载hdfs的文件
println("lines rdd's partition is: " + lines.getNumPartitions)
//calc word's count
val words:RDD[String] = lines.flatMap(line => line.split("\\s+"))
val pairs:RDD[(String, Int)] = words.map(word => (word, 1))
val ret:RDD[(String, Int)] = pairs.reduceByKey(myReduceFunc)
//export data to external system
ret.foreach{case (word, count) => {
println(word + "--->" + count)
}}
//ret.saveAsTextFile(output)
sc.stop()
}
def myReduceFunc(v1: Int, v2: Int): Int = {
// val i = 1 / 0 //为了验证lazy
v1 + v2
}
}
6.提交到spark集群
spark-submit-wc-standalone-client.sh
#!/bin/sh
SPARK_HOME=/opt/module/spark-2.4.3
${SPARK_HOME}/bin/spark-submit \
--class com.offcn.bigdata.spark.RemoteScalaWordCountApp \
--master spark://hadoop003:7077 \
--deploy-mode client \
--executor-cores 1 \
--total-executor-cores 2 \
--driver-memory 600M \
--executor-memory 600M \
/root/spark/wc.jar \
hdfs://hadoop003:9000/test/input/wordcount.txt
spark-submit-wc-standalone-cluster.sh
将driver提交到集群运行,driver会读取不到本地的wc.jar,所以要把wc.jar提交到hdfs
#!/bin/sh
SPARK_HOME=/opt/module/spark-2.4.3
${SPARK_HOME}/bin/spark-submit \
--class com.offcn.bigdata.spark.RemoteScalaWordCountApp \
--master spark://hadoop003:7077 \
--deploy-mode cluster \
--driver-cores 1 \
--executor-cores 1 \
--total-executor-cores 2 \
--driver-memory 600M \
--executor-memory 600M \
hdfs://hadoop003:9000/spark/wc.jar \
hdfs://hadoop003:9000/test/input/wordcount.txt
7.疑惑
一个疑惑的问题,在local的方式中,可以看到foreach的结果,但是client模式下面就看不到这个结果,这是为何?
算子操作都是在worker中执行的,包括foreach操作,所以要想看结果就应该去worker上面查看,而不是提交作业的本机。
8.提交到yarn集群
spark基于yarn集群的运行方式,是在国内最常见的一种方式。
package com.offcn.bigdata.spark
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* @author ljh
* @create 2020-12-06 18:17
*/
object RemoteScalaWordCountApp {
def main(args: Array[String]): Unit = {
if(args == null || args.length != 2) {
println(
"""
|Usage: <input><output>
|""".stripMargin)
System.exit(-1)
}
val Array(input,output) = args
val conf = new SparkConf()
.setAppName(s"${RemoteScalaWordCountApp.getClass.getSimpleName}")
val sc = new SparkContext(conf)
val lines: RDD[String] = sc.textFile(input)//加载hdfs的文件
println("lines rdd's partition is: " + lines.getNumPartitions)
//calc word's count
val words:RDD[String] = lines.flatMap(line => line.split("\\s+"))
val pairs:RDD[(String, Int)] = words.map(word => (word, 1))
val ret:RDD[(String, Int)] = pairs.reduceByKey(myReduceFunc)
//export data to external system
ret.foreach{case (word, count) => {
println(word + "--->" + count)
}}
ret.saveAsTextFile(output)
sc.stop()
}
def myReduceFunc(v1: Int, v2: Int): Int = {
// val i = 1 / 0 //为了验证lazy
v1 + v2
}
}
spark-submit-wc-yarn-client.sh
两个参数,指定输入和输出的位置
#!/bin/sh
SPARK_HOME=/opt/module/spark-2.4.3
${SPARK_HOME}/bin/spark-submit \
--class com.offcn.bigdata.spark.RemoteScalaWordCountApp \
--master yarn \
--deploy-mode client \
--executor-cores 1 \
--num-executors 1 \
--driver-memory 600M \
--executor-memory 600M \
/root/spark/wc1.jar \
hdfs://hadoop003:9000/test/input/wordcount.txt \
hdfs://hadoop003:9000/spark/wc
spark-submit-wc-yarn-cluster.sh
#!/bin/sh
SPARK_HOME=/opt/module/spark-2.4.3
${SPARK_HOME}/bin/spark-submit \
--class com.offcn.bigdata.spark.RemoteScalaWordCountApp \
--master yarn \
--deploy-mode cluster \
--driver-cores 1 \
--executor-cores 1 \
--num-executors 1 \
--driver-memory 600M \
--executor-memory 600M \
hdfs://hadoop003:9000/spark/wc1.jar \
hdfs://hadoop003:9000/test/input/wordcount.txt \
hdfs://hadoop003:9000/spark/wc1
9.报错
大家直接基于yarn模式进行运行,肯定会报错,一般报的错误就是虚拟内存超过的物理内存这个错误。
Container killed by YARN for exceeding memory limits. 15.6 GB of 15.5 GB physical memory used.
解决问题