第1章 环境准备
“凡事豫则立,不豫则废;言前定,则不跲;事前定,则不困;”
——《礼记·中庸》
本章导读:
在深入了解一个系统的原理、实现细节之前,应当先准备好它的源码编译环境、运行环境。如果能在实际环境安装和运行Spark,显然能够提升读者对于Spark的一些感受,对系统能有个大体的印象,有经验的技术人员甚至能够猜出一些Spark采用的编程模型、部署模式等。当你通过一些途径知道了系统的原理之后,难道不会问问自己?这是怎么做到的。如果只是游走于系统使用、原理了解的层面,是永远不可能真正理解整个系统的。很多IDE本身带有调试的功能,每当你阅读源码,陷入重围时,调试能让我们更加理解运行期的系统。如果没有调试功能,不敢想象阅读源码的困难。本章的主要目的是帮助读者构建源码学习环境,主要包括以下内容:在windows环境下搭建源码阅读环境;在Linux搭建基本的执行环境;Spark的基本使用,如spark-shell。
1.1 运行环境准备
考虑到大部分公司在开发和生成环境都采用Linux操作系统,所以笔者选用了64位的Linux。在正式安装Spark之前,先要找台好机器。为什么?因为笔者在安装、编译、调试的过程中发现Spark非常耗费内存,如果机器配置太低,恐怕会跑不起来。Spark的开发语言是Scala,而Scala需要运行在JVM之上,因而搭建Spark的运行环境应该包括JDK和Scala。
1.1.1 安装JDK
使用命令getconf LONG_BIT查看linux机器是32位还是64位,然后下载相应版本的JDK并安装。下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html。
配置环境:
cd ~
vim .bash_profile
添加如下配置:
export JAVA_HOME=/opt/java
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
由于笔者的机器上已经安装过openjdk,安装命令:
$ su -c "yum install java-1.7.0-openjdk"
安装完毕后,使用java –version命令查看,确认安装正常,如图1-1所示。
图1-1 查看java安装是否正常
1.1.2 安装Scala
下载地址:http://www.scala-lang.org/download/
选择最新的Scala版本下载,下载方法如下:
wget http://downloads.typesafe.com/scala/2.11.5/scala-2.11.5.tgz
移动到选好的安装目录,例如:
mv scala-2.11.5.tgz ~/install/
进入安装目录,执行以下命令:
chmod 755 scala-2.11.5.tgz
tar -xzvf scala-2.11.5.tgz
配置环境:
cd ~
vim .bash_profile
添加如下配置:
export SCALA_HOME=$HOME/install/scala-2.11.5
export PATH=$PATH:$SCALA_HOME/bin:$HOME/bin
安装完毕后键入scala,进入scala命令行,如图1-2所示。
图1-2 进入Scala命令行
1.1.3安装Spark
下载地址:http://spark.apache.org/downloads.html
选择最新的Spark版本下载,下载方法如下:
wget http://archive.apache.org/dist/spark/spark-1.2.0/spark-1.2.0-bin-hadoop1.tgz
移动到选好的安装目录,如:
mv spark-1.2.0-bin-hadoop1.tgz~/install/
进入安装目录,执行以下命令:
chmod 755 spark-1.2.0-bin-hadoop1.tgz
tar -xzvf spark-1.2.0-bin-hadoop1.tgz
配置环境:
cd ~
vim .bash_profile
添加如下配置:
export SPARK_HOME=$HOME/install/spark-1.2.0-bin-hadoop1
1.2 SPARK初体验
本节通过Spark的基本使用,让读者对Spark能有初步的认识,便于引导读者逐步深入学习。
1.2.1 运行spark-shell
要运行spark-shell,需要先对Spark进行配置。
进入Spark的conf文件夹:
cd ~/install/spark-1.2.0-bin-hadoop1/conf
拷贝一份spark-env.sh.template,命名为spark-env.sh,对它进行编辑,命令如下:
cp spark-env.sh.template spark-env.sh
vim spark-env.sh
添加如下配置:
export SPARK_MASTER_IP=127.0.0.1
export SPARK_LOCAL_IP=127.0.0.1
启动spark-shell:
cd ~/install/spark-1.2.0-bin-hadoop1/bin
./spark-shell
最后我们会看到spark启动的过程,如图1-3所示:
图1-3 Spark启动过程
从以上启动日志中我们可以看到SparkEnv、MapOutputTracker、BlockManagerMaster、DiskBlockManager、MemoryStore、HttpFileServer、SparkUI等信息。它们是做什么的?此处望文生义即可,具体内容将在后边的章节详细给出。
1.2.2 执行word count
这一节,我们通过word count这个耳熟能详的例子来感受下Spark任务的执行过程。启动spark-shell后,会打开Scala命令行,然后按照以下步骤输入脚本:
步骤1 输入val lines = sc.textFile("../README.md", 2),执行结果如图1-4所示。
图1-4 步骤1执行结果
步骤2 输入val words = lines.flatMap(line => line.split(" ")),执行结果如图1-5所示。
图1-5 步骤2执行结果
步骤3 输入val ones = words.map(w => (w,1)),执行结果如图1-6所示。
图1-6 步骤3执行结果
步骤4 输入val counts = ones.reduceByKey(_ + _),执行结果如图1-7所示。
图1-7 步骤4执行结果
步骤5 输入counts.foreach(println),任务执行过程如图1-8和图1-9所示。输出结果如图1-10所示。
图1-8 步骤5执行过程部分
图1-9 步骤5执行过程部分
图1-10 步骤5输出结果
在这些输出日志中,我们先是看到Spark中任务的提交与执行过程,然后看到单词计数的输出结果,最后打印一些任务结束的日志信息。有关任务的执行分析,笔者将在第5章中展开。
1.2.3 剖析spark-shell
通过word count在spark-shell中执行的过程,我们想看看spark-shell做了什么?spark-shell中有以下一段脚本,见代码清单1-1。
代码清单1-1 spark-shell
function main() {
if $cygwin; then
stty -icanonmin 1 -echo > /dev/null 2>&1
export SPARK_SUBMIT_OPTS="$SPARK_SUBMIT_OPTS -Djline.terminal=unix"
"$FWDIR"/bin/spark-submit --class org.apache.spark.repl.Main "${SUBMISSION_OPTS[@]}" spark-shell "${APPLICATION_OPTS[@]}"
sttyicanon echo > /dev/null 2>&1
else
export SPARK_SUBMIT_OPTS
"$FWDIR"/bin/spark-submit --class org.apache.spark.repl.Main "${SUBMISSION_OPTS[@]}" spark-shell "${APPLICATION_OPTS[@]}"
fi
}
我们看到脚本spark-shell里执行了spark-submit脚本,那么打开spark-submit脚本,发现其中包含以下脚本。
exec "$SPARK_HOME"/bin/spark-class org.apache.spark.deploy.SparkSubmit "${ORIG_ARGS[@]}"
脚本spark-submit在执行spark-class脚本时,给它增加了参数SparkSubmit 。打开spark-class脚本,其中包含以下脚本,见代码清单1-2。
代码清单1-2 spark-class
if [ -n "${JAVA_HOME}" ]; then
RUNNER="${JAVA_HOME}/bin/java"
else
if [ `command -v java` ]; then
RUNNER="java"
else
echo "JAVA_HOME is not set" >&2
exit 1
fi
fi
exec "$RUNNER" -cp "$CLASSPATH" $JAVA_OPTS "$@"
读到这,应该知道Spark启动了以SparkSubmit为主类的jvm进程。为便于在本地能够对Spark进程使用远程监控,给spark-class脚本增加追加以下jmx配置:
JAVA_OPTS="-XX:MaxPermSize=128m $OUR_JAVA_OPTS -Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=10207 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
在本地打开jvisualvm,添加远程主机,如图1-11所示:
图1-11 添加远程主机
右键单击已添加的远程主机,添加JMX连接,如图1-12:
图1-12 添加JMX连接
选择右侧的“线程”选项卡,选择main线程,然后点击“线程Dump”按钮,如图1-13。
图1-13 查看Spark线程
从dump的内容中找到线程main的信息如代码清单1-3所示。
代码清单1-3 main线程dump信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|