这次我们主要介绍spark的任务提交的方式和代码的编写
- spark的常用角色介绍
- spark-shell
- 代码任务提交
1. spark常用的角色介绍
Spark 是基于内存计算的大数据并行计算框架。 因为其基于内存计算, 比Hadoop 中 MapReduce 计算框架具有更高的实时性, 同时保证了高效容错性和可伸缩性。 从 2009 年诞生于 AMPLab 到现在已经成为 Apache 顶级开源项目, 并成功应用于商业集群中, 学习 Spark 就需要了解其架构。
Spark 架构图如下:
Spark 架构使用了分布式计算中master-slave模型,master是集群中含有master进程的节点,slave 是集群中含有worker进程的节点。
- Driver-Program:运行main函数并且新建SparkContext的程序。
- Application:基于Spark的应用程序,包含了driver程序和集群上的executor。
- Cluster Manager:指的是在集群上获取资源的外部服务。目前有三种类型
- Standalone:spark原生的资源管理,由Master负责资源的分配
- Apache Mesos:与hadoop、MR兼容性良好的一种资源调度框架
- Hadoop Yarn:主要是指Yarn中的ResourceManager
- Worker Node:集群中任何可以运行Application代码的节点,在Standalone模式中指的是通过 slaves 文件配置的 Worker 节点, 在 Spark on Yarn 模式下就是 NodeManager 节点
- Executor:是在一个worker-node上为某应⽤启动的⼀个进程,该进程负责运行任务, 并且负责将数据存在内存或者磁盘上。 每个应⽤都有各自独立的 executor。
- Task:被送到某个executor上的工作单元。
2. spark-shell
spark-shell 是 Spark 自带的交互式 Shell 程序, 方便用户进行交互式编程, 用户可以在该命令行下用 scala 编写 spark 程序。
2.1 读取本地文件
单机模式: 通过本地 N 个线程跑任务, 只运行一个 SparkSubmit 进程。
- 需求
读取本地文件a.txt, 实现文件内的单词计数。 本地文件 words.txt 内容如下:
hello me
hello you
hello her
- 实现
//1.启动shell命令
$sprak/bin/spark-shell --master local[2]
//2. 输入读取命令
sc.textFile("file:///root///temp///a.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
//代码说明
1. sc: Spark-Shell 中已经默认将 SparkContext 类初始化为对象 sc。 用户代码如
果需要用到, 则直接应用 sc 即可。
2. textFile:读取数据文件
3. flatMap:对文件中的每一行数据进行压平切分,这里按照空格分隔。
4. map:对出现的每一个单词记为 1(word, 1)
5. reduceByKey:对相同的单词出现的次数进行累加
6. collect:触发任务执行, 收集结果数据。
- 运行结果
2.2 读取HDFS的文件
需求不变,这里需要修改配置文件
//1. spark与hadoop整合,修改配置文件 spark-env.sh
#添加 HADOOP_CONF_DIR 指定hadoop集群中配置文件的路径
export HADOOP_CONF_DIR=/export/server/hadoop-2.7.4/etc/hadoop
//2. 启动HDFS,重启spark
//3. 上传a.txt到HDFS目录
hadoop fs -put a.txt /
//4. 输入下面的命令进行计算
sc.textFile("/a.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
2.3 运行 spark-shell 指定具体的 master 地址
- 需求
spark-shell 运行时指定具体的 master 地址, 读取 HDFS 上的数据, 做单词计数, 然后将结果保存在 HDFS 上。 - 启动的执行命令
spark-shell \
--master spark://nodel01:7077 \
--executor-memory 1g \
--total-executor-cores 2
参数说明:
--master spark://hdp-node-01:7077 指定 Master 的地址
--executor-memory 1g 指定每个 worker 可用内存为 1g
--total-executor-cores 2 指定整个集群使用的 cup 核数为 2 个
注意:
如果启动 spark shell 时没有指定 master 地址, 但是也可以正常启动 spark shell和执行 spark shell 中的程序, 其实是启动了 spark 的 local 模式, 该模式仅在本机启动一个进程, 没有与集群建立联系。
- 执行运算的命令
sc.textFile("/a.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).saveAsTextFile("/wc")
- 在HDFS的查看运行的结果
3 .代码示例
spark-shell 仅在测试和验证我们的程序时使用的较多, 在生产环境中, 通常会在 IDEA 中编写程序, 然后打成 jar 包, 最后提交到集群。 最常用的是创建一个Maven 项目, 利用 Maven 来管理 jar 包的依赖。
3.1 Scala代码
- pom依赖
<properties>
<scala.version>2.11.8</scala.version>
<hadoop.version>2.7.4</hadoop.version>
<spark.version>2.0.2</spark.version>
</properties>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</args>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass></mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
- Scala代码
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
//todo:需求:利用scala语言实现spark wordcount程序
object WordCount_Online {
def main(args: Array[String]): Unit = {
//1、创建sparkConf对象,设置appName和master的地址
val sparkConf: SparkConf = new SparkConf().setAppName("WordCount_Online")
//2、创建sparkcontext对象
val sc = new SparkContext(sparkConf)
//设置日志输出级别
sc.setLogLevel("WARN")
// 3、读取数据文件
val data: RDD[String] = sc.textFile(args(0))
//4、切分文件中的每一行,返回文件所有单词
val words: RDD[String] = data.flatMap(_.split(" "))
//5、每个单词记为1,(单词,1)
val wordAndOne: RDD[(String, Int)] = words.map((_,1))
//6、相同单词出现的次数累加
val result: RDD[(String, Int)] = wordAndOne.reduceByKey(_+_)
//按照单词出现的次数降序排列
val sortResult: RDD[(String, Int)] = result.sortBy(_._2,false)
//7、结果数据保存在HDFS上
sortResult.saveAsTextFile(args(1))
//8、关闭sc
sc.stop()
}
}
- 打包完成后,直接提交代码到集群上运行,命令如下
spark-submit --class com.wordcount.WordCount_Online
--master spark://node1:7077
--executor-memory 1G
--total-executor-cores 2
/root/original-spark-2.0.jar
/words.txt /out100
Java代码
- 在相同的项目,使用相同的pom
- 代码如下
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
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 scala.Tuple2;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
//todo:需求:利用java语言开发spark wordcount程序
public class WordCount_Java {
public static void main(String[] args) {
//1、创建sparkConf对象
SparkConf sparkConf = new SparkConf().setAppName("WordCount_Java").setMaster("local[2]");
//2、创建sparkContext对象
JavaSparkContext jsc = new JavaSparkContext(sparkConf);
//3、读取数据文件
JavaRDD<String> dataJavaRDD = jsc.textFile("d:\\words.txt");
//4、切分每一行
JavaRDD<String> wordsJavaRDD = dataJavaRDD.flatMap(new FlatMapFunction<String, String>() {
public Iterator<String> call(String line) throws Exception {
String[] words = line.split(" ");
return Arrays.asList(words).iterator();
}
});
//5、每个单词记为1
JavaPairRDD<String, Integer> wordAndOneJavaPairRDD = wordsJavaRDD.mapToPair(new PairFunction<String, String, Integer>() {
public Tuple2<String, Integer> call(String word) throws Exception {
return new Tuple2<String, Integer>(word, 1);
}
});
//6、相同单词出现的次数累加
JavaPairRDD<String, Integer> resultJavaPairRDD = wordAndOneJavaPairRDD.reduceByKey(new Function2<Integer, Integer, Integer>() {
public Integer call(Integer v1, Integer v2) throws Exception {
return v1 + v2;
}
});
//按照单词出现的次数进行降序排列,思想:先把(单词,次数)位置颠倒(次数,单词)
JavaPairRDD<Integer, String> reverseJavaPairRDD = resultJavaPairRDD.mapToPair(new PairFunction<Tuple2<String, Integer>, Integer, String>() {
public Tuple2<Integer, String> call(Tuple2<String, Integer> tuple2) throws Exception {
return new Tuple2<Integer, String>(tuple2._2, tuple2._1);
}
});
// 按照单词出现的次数进行降序排列 调用sortByKey方法,然后将(次数,单词)颠倒为(单词,次数)
JavaPairRDD<String, Integer> sortJavaPairRDD = reverseJavaPairRDD.sortByKey(false).mapToPair(new PairFunction<Tuple2<Integer, String>, String, Integer>() {
public Tuple2<String, Integer> call(Tuple2<Integer, String> tuple2) throws Exception {
return new Tuple2<String, Integer>(tuple2._2, tuple2._1);
}
});
//7、收集结果数据
List<Tuple2<String, Integer>> finalResult = sortJavaPairRDD.collect();
//8、循环打印结果数据
for(Tuple2<String, Integer> tuple:finalResult){
System.out.println(tuple._1+"出现的次数"+tuple._2);
}
//9、关闭sparkcontext
jsc.stop();
}
}
- 打包提交任务的命令相同
本次介绍结束