Spark
1.spark 概述
- spark概念
- 基于内存的分布式计算系统,计算速度很快,只是用于数据的计算,不涉及到数据的存储.可以对接外部数据源(例如HDFS)
- Spark是一个开源的类似于Hadoop MapReduce的通用的并行计算框架,Spark基于map reduce算法实现的分布式计算,拥有Hadoop MapReduce所具有的优点;但不同于MapReduce的是Spark中的Job中间输出和结果可以保存在内存中,从而不再需要读写HDFS,因此Spark能更好地适用于数据挖掘与机器学习等需要迭代的map reduce的算法。
- Spark是MapReduce的替代方案,而且兼容HDFS、Hive,可融入Hadoop的生态系统,以弥补MapReduce的不足。
2.spark特性
- 速度快
- 与Hadoop的MapReduce相比,Spark基于内存的运算要快100倍以上,基于硬盘的运算也要快10倍以上。Spark实现了高效的DAG执行引擎,可以通过基于内存来高效处理数据流。
- 在MapReduce任务中,如果当前有100个task,需要100个进程去执行task(MapReduce是以进程的方式运行任务)
- 在spark任务中,如果当前有100个task,需要100个线程去执行task(spark是以线程的方式运行任务)
- 易用性
- 可以快速编写spark应用程序,使用四中语言 Java/scala/pathon/R.还支持超过80种高级算法,使用户可以快速构建不同的应用。
- 通用性
- Spark可以用于批处理、交互式查询(Spark SQL)、实时流处理(Spark Streaming)、机器学习(Spark MLlib)和图计算(GraphX)。这些不同类型的处理都可以在同一个应用中无缝使用。
- 兼容性
- Spark可以使用Hadoop的YARN和Apache Mesos作为它的资源管理和调度器,并且可以处理所有Hadoop支持的数据,包括HDFS、HBase和Cassandra等。
- Spark也可以不依赖于第三方的资源管理和调度器,它实现了Standalone作为其内置的资源管理和调度框架,这样进一步降低了Spark的使用门槛,使得所有人都可以非常容易地部署和使用Spark。此外,Spark还提供了在EC2上部署Standalone的Spark集群的工具。
3.spark安装
- 1.解压安装包
tar -zxvf spark-2.0.2-bin-hadoop2.7.tgz -C /usr/local/soft
- 2.修改配置文件
- 配置文件目录在 /usr/local/soft/spark/conf
- vi spark-env.sh 修改文件(先把spark-env.sh.template重命名为spark-env.sh)
#配置java环境变量
export JAVA_HOME=/usr/local/soft/jdk1.7.0_67
#指定spark老大Master的IP
export SPARK_MASTER_HOST=node1
#指定spark老大Master的端口
export SPARK_MASTER_PORT=7077
vi slaves 修改文件(先把slaves.template重命名为slaves)
node2
node3
- 3.拷贝配置到其他主机
通过scp 命令将spark的安装目录拷贝到其他机器上
scp -r /usr/local/soft/spark node2:/usr/local/soft
scp -r /usr/local/soft/spark node3:/usr/local/soft
- 4.配置spark环境变量
- 将spark添加到环境变量,添加以下内容到 /etc/profile
export SPARK_HOME=/usr/local/soft/spark
export PATH=$PATH:$SPARK_HOME/bin:$SPARK_HOME/sbin
#之后再把/etc/profile分发到node2和node3上
scp /etc/profile node2:/etc/
scp /etc/profile node3:/etc/
注意最后 source /etc/profile 刷新配置
3.1启动spark
- #在主节点上启动spark,配置环境变量可以直接启动
/usr/local/soft/sbin/start-all.sh
注意 启动命令与hadoop相同,一般通过局部路径启动或者绝对路径启动
3.2 停止spark
- #在主节点上停止spark集群
/usr/local/soft/spark/sbin/stop-all.sh
注意 关闭命令与hadoop相同,一般通过局部路径关闭或者绝对路径关闭
3.3spark的web界面
- 正常启动spark集群后,可以通过访问http://node1:8080,查看spark的web界面,查看相关信息。
4.Spark HA高可用部署
4.1 高可用部署说明
- SparkStandalone集群是Master-Slaves架构的集群模式,和大部分的Master-Slaves结构集群一样,存在着Master单点故障的问题。如何解决这个单点故障的问题,Spark提供了两种方案:
- (1)基于文件系统的单点恢复(Single-NodeRecovery with Local File System)。
主要用于开发或测试环境。当spark提供目录保存spark Application和worker的注册信息,并将他们的恢复状态写入该目录中,这时,一旦Master发生故障,就可以通过重新启动Master进程(sbin/start-master.sh),恢复已运行的spark Application和worker的注册信息。
- (2)基于zookeeper的StandbyMasters(Standby Masters with ZooKeeper)。
用于生产模式。其基本原理是通过zookeeper来选举一个Master,其他的Master处于Standby状态。将spark集群连接到同一个ZooKeeper实例并启动多个Master,利用zookeeper提供的选举和状态保存功能,可以使一个Master被选举成活着的master,而其他Master处于Standby状态。如果现任Master死去,另一个Master会通过选举产生,并恢复到旧的Master状态,然后恢复调度。整个恢复过程可能要1-2分钟。
4.2 基于zookeeper的Spark HA高可用集群部署
- 该HA方案使用起来很简单,首先需要搭建一个zookeeper集群,然后启动zooKeeper集群,最后在不同节点上启动Master。具体配置如下:
vim spark-env.sh
注释掉 export SPARK_MASTER_HOST=hdp-node-01
在spark-env.sh添加SPARK_DAEMON_JAVA_OPTS,内容如下:
export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER
-Dspark.deploy.zookeeper.url=node1:2181,node2:2181,node3:2181
-Dspark.deploy.zookeeper.dir=/spark"
- 参数说明
- spark.deploy.recoveryMode:恢复模式(Master重新启动的模式)
- 有三种:(1)ZooKeeper(2) FileSystem (3)NONE
- spark.deploy.zookeeper.url:ZooKeeper的Server地址
- spark.deploy.zookeeper.dir:保存集群元数据信息的文件、目录。包括Worker,Driver和Application。
注意:
- 在普通模式下启动spark集群,只需要在主机上面执行start-all.sh 就可以了。
- 在高可用模式下启动spark集群,先需要在任意一台节点上启动start-all.sh命令。然后在另外一台节点上单独启动master。命令start-master.sh。
可以在zkCli.sh 中查看zookeeper根目录是否有spark的存储信息
结果如下:
[zk: localhost:2181(CONNECTED) 2] ls /
[cluster, brokers, storm, zookeeper, admin, isr_change_notification, log_dir_event_notification, controller_epoch, spark, kafka-manager, consumers, latest_producer_id_block, config]
5.spark各个角色介绍
Spark架构使用了分布式计算中master-slave模型,master是集群中含有master进程的节点,slave是集群中含有worker进程的节点。
- Driver Program :运⾏main函数并且新建SparkContext的程序。
- Application:基于Spark的应用程序,包含了driver程序和集群上的executor。
- Cluster Manager:指的是在集群上获取资源的外部服务。目前有三种类型
(1)Standalone:spark原生的资源管理,由Master负责资源的分配
(2)ApacheMesos:与hadoop MR兼容性良好的一种资源调度框架
(3)HadoopYarn: 主要是指Yarn中的ResourceManager
- Worker Node: 集群中任何可以运行Application代码的节点,在Standalone模式中指的是通过slaves文件配置的Worker节点,在Spark on Yarn模式下就是NodeManager节点
- Executor:是在一个worker node上为某应⽤启动的⼀个进程,该进程负责运⾏行任务,并且负责将数据存在内存或者磁盘上。每个应⽤都有各自独立的executor。
- Task :被送到某个executor上的工作单元。
6.spark程序
- 1.普通模式提交(指定当前集群中或者的master地址)
bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://node1:7077 \
--executor-memory 1G \
--total-executor-cores 2 \
examples/jars/spark-examples_2.11-2.3.0.jar \
10
#spark2.3 jar包名字 : spark-examples_2.11-2.3.0.jar
该算法是利用蒙特·卡罗算法求圆周率PI,通过计算机模拟大量的随机数,最终会计算出比较精确的π。
- 2.高可用模式提交
- 在高可用模式下,因为涉及到多个Master,所以对于应用程序的提交就有了一点变化,因为应用程序需要知道当前的Master的IP地址和端口。这种HA方案处理这种情况很简单,只需要在SparkContext指向一个Master列表就可以了,如spark://host1:port1,host2:port2,host3:port3,应用程序会轮询列表,找到活着的Master。
bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://node1:7077,node2:7077,node3:7077 \
--executor-memory 1G \
--total-executor-cores 2 \
examples/jars/spark-examples_2.11-2.0.2.jar \
10
- 可以修改log4j控制台输出的命令
# cp log4j.properties.template log4j.properties
#将INFO改为WARN
log4j.rootCategory=WARN, 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
7.spark-shell使用
- 1、spark-shell –master local[2] 读取本地数据文件实现单词计数
- local[N]
- local表示本地运行,后面的数字N表示本地采用多少个线程去运行任务
- local[*]
- local表示本地运行,后面的* 表示使用当前机器上所有可用的资源
- 他会产生一个SparkSubmit进程。
sc.textFile("file:///root/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
代码说明:
- sc:Spark-Shell中已经默认将SparkContext类初始化为对象sc。用户代码如果需要用到,则直接应用sc即可。
- textFile:读取数据文件
- flatMap:对文件中的每一行数据进行压平切分,这里按照空格分隔。
- map:对出现的每一个单词记为1(word,1)
- reduceByKey:对相同的单词出现的次数进行累加
- collect:触发任务执行,收集结果数据。
2、spark-shell –master local[2] 读取HDFS数据文件实现单词计数
sc.textFile("hdfs://node1:9000/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
- spark整合HDFS
- 需要修改spark-env.sh
export HADOOP_CONF_DIR=/usr/local/soft/hadoop-2.7.4/etc/hadoop
- 3、spark-shell –master spark://node1:7077 读取HDFS数据文件实现单词计数
sc.textFile("/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
- 4.运行spark-shell 指定具体的master地址
(1)需求: spark-shell运行时指定具体的master地址,读取HDFS上的数据,做单词计数,然后将结果保存在HDFS上。
(2)执行启动命令:
spark-shell \
--masterspark://node1: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和执行sparkshell中的程序,其实是启动了spark的local模式,该模式仅在本机启动一个进程,没有与集群建立联系
- 执行代码
sc.textFile("/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).saveAsTextFile("/wc")
8.利用scala编写spark的wordcount程序(本地运行)
- 导入依赖
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.11.8</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.4</version>
</dependency>
- 代码开发
package cn.itcast
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
//todo:利用scala实现spark的wordcount程序
object WordCount {
def main(args: Array[String]): Unit = {
//1、创建sparkconf 设置appName和master地址 local[2] 表示本地使用2个线程运行
val sparkConf: SparkConf = new SparkConf().setAppName("WordCount").setMaster("local[2]")
//2、创建sparkcontext,所有计算的源头,它会创建DAGScheduler和TaskScheduler
val sc = new SparkContext(sparkConf)
//3、读取数据文件
val data: RDD[String] = sc.textFile("D:\\words.txt")
//4、切分每一行
val words: RDD[String] = data.flatMap(_.split(" "))
//5、每个单词计为1
val wordAndOne: RDD[(String, Int)] = words.map((_,1))
//6、相同单词出现次数累加
val result: RDD[(String, Int)] = wordAndOne.reduceByKey(_+_)
//7、打印输出
val finalResult: Array[(String, Int)] = result.collect()
println(finalResult.toBuffer)
//关闭sc
sc.stop()
}
}
9.利用scala编写spark的wordcount程序(集群运行)
package cn.itcast
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
//todo:利用scala实现spark的wordcount程序(集群运行)
object WordCountOnlinux {
def main(args: Array[String]): Unit = {
//1、创建sparkconf 设置appName
val sparkConf: SparkConf = new SparkConf().setAppName("WordCount_Online")
//2、创建sparkcontext,所有计算的源头,它会创建DAGScheduler和TaskScheduler
val sc = new SparkContext(sparkConf)
//3、读取数据文件
val data: RDD[String] = sc.textFile(args(0))
//4、切分每一行
val words: RDD[String] = data.flatMap(_.split(" "))
//5、每个单词计为1
val wordAndOne: RDD[(String, Int)] = words.map((_,1))
//6、相同单词出现次数累加
val result: RDD[(String, Int)] = wordAndOne.reduceByKey(_+_)
//7、保存结果数据到HDFS上
result.saveAsTextFile(args(1))
//关闭sc
sc.stop()
}
}
- 提交脚本(包是包含依赖的jar包)
spark-submit --class cn.itcast.WordCountOnlinux --master spark://node1:7077 --executor-memory 1g --total-executor-cores 2 spark-mytest-1.0-SNAPSHOT.jar /words.txt /2018
10.利用java编写spark的wordcount程序(本地运行)
package cn.itcast;
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、创建javaSparkContext
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> reverseJavaRDD = resultJavaPairRDD.mapToPair(new PairFunction<Tuple2<String, Integer>, Integer, String>() {
public Tuple2<Integer, String> call(Tuple2<String, Integer> t) throws Exception {
return new Tuple2<Integer, String>(t._2, t._1);
}
});
//按照单词出现的次数降序排列 需要把(次数,单词)位置颠倒(单词,次数)
JavaPairRDD<String, Integer> sortedRDD = reverseJavaRDD.sortByKey(false).mapToPair(new PairFunction<Tuple2<Integer, String>, String, Integer>() {
public Tuple2<String, Integer> call(Tuple2<Integer, String> t) throws Exception {
return new Tuple2<String, Integer>(t._2, t._1);
}
});
//7、打印结果
List<Tuple2<String, Integer>> finalResult = sortedRDD.collect();
for (Tuple2<String, Integer> tuple : finalResult) {
System.out.println("单词:"+tuple._1+" 次数:"+tuple._2);
}
//8、关闭
jsc.stop();
}
}