天天看點

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

  • ​​源碼下載下傳​​
  • ​​源碼編譯​​
  • ​​maven 下載下傳​​
  • ​​scala 下載下傳​​
  • ​​編譯參數​​
  • ​​編譯​​
  • ​​編譯分發的二進制包​​
  • ​​單機啟動​​
  • ​​叢集部署​​
  • ​​開發環境內建​​
  • ​​源碼編譯的3.2.0版本無法在window上直接用spark-shell啟動​​
  • ​​總結​​

項目位址:https://gitee.com/jyq_18792721831/studyspark.git

源碼下載下傳

打開​​Apache Spark™ - Unified Engine for large-scale data analytics​​,下載下傳源碼

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

在下載下傳界面中選擇源碼下載下傳

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

随便選擇哪個位址

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

下載下傳源碼後上傳到hadoop01伺服器的​

​/spark​

​目錄下

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

使用​

​tar -zxvf spark-3.2.0.tgz​

​​解壓到​

​/spark​

​目錄下

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

源碼編譯

傳回下載下傳界面,選擇文檔,選擇最新版或者你下載下傳的版本

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

或者你可以直接打開​​Overview - Spark 3.2.0 Documentation (apache.org)​​界面

選擇跳轉到打包建構的文檔

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

有兩種建構方式:maven和sbt(scala build tool)

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

我們選擇maven

maven 下載下傳

在編譯文檔中,要求我們盡可能使用新的maven

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

是以我們使用手動安裝的方式,安裝最新版

首先打開​​Maven – Welcome to Apache Maven​​,跳轉到下載下傳頁

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

我們選擇最新的二進制包

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

右鍵拷貝連結,直接在hadoop01上下載下傳

在根目錄下建立​

​/maven​

​​目錄,然後執行​

​curl -O https://dlcdn.apache.org/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz​

​下載下傳

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

使用​

​tar -zxvf apache-maven-3.8.4-bin.tar.gz​

​解壓

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

删除壓縮包,并把maven裡面的内容移動出來,使得maven的home為​

​/maven​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

因為之前就在hadoop01上安裝過了java,并且配置了環境變量,是以可以直接配置maven的環境變量即可,高版本需要jdk1.7以上

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

配置環境變量

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後使用​

​source ~/.bash_profile​

​生效

使用​

​mvn --version​

​驗證版本

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

scala 下載下傳

到​​The Scala Programming Language (scala-lang.org)​​進入下載下傳界面

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

選擇scala2吧,scala3實在沒用過。

拉到最下面,下載下傳二進制包

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

相同的方式,拷貝連結,在hadoop01中下載下傳

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後配置環境變量

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

使用​

​source ~/.bash_profile​

​生效後驗證版本

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

編譯參數

根據文檔,首先設定maven的資訊

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

我們将​

​MAVEN_OPTS​

​​放入​

​~/.bash_profile​

​中

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

使用​

​source ~/.bash_profile​

​使之生效。

接着往下看建構文檔,找到支援hive和jdbc的建構

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

這是maven指令,使用源碼包中自帶的maven,其中​

​-P​

​​和​

​-D​

​是一些參數。

這些參數和pom.xml檔案中的profile和properties有關。

​-D​

​對應properties

​-P​

​對應profile

我們檢視pom.xml檔案

這隻是一部分,具體的可以自行檢視

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <!-- 我們使用的是java 11,這個需要進行替換 -->
    <java.version>1.8</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <!-- 因為我們使用的是3.8.1的maven,是以這個也需要替換 -->
    <maven.version>3.6.3</maven.version>
    <exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
    <sbt.project.name>spark</sbt.project.name>
    <slf4j.version>1.7.30</slf4j.version>
    <log4j.version>1.2.17</log4j.version>
    <!-- hadoop 的版本是2.9.2 -->
    <hadoop.version>3.3.1</hadoop.version>
    <protobuf.version>2.5.0</protobuf.version>
    <yarn.version>${hadoop.version}</yarn.version>
    <zookeeper.version>3.6.2</zookeeper.version>
    <curator.version>2.13.0</curator.version>
    <hive.group>org.apache.hive</hive.group>
    <hive.classifier>core</hive.classifier>
    <!-- Version used in Maven Hive dependency -->
    <!-- hive的版本是2.3.9,這個可以不用修改 -->
    <hive.version>2.3.9</hive.version>
    <hive23.version>2.3.9</hive23.version>
    <!-- Version used for internal directory structure -->
    <hive.version.short>2.3</hive.version.short>
    <!-- note that this should be compatible with Kafka brokers version 0.10 and up -->
    <kafka.version>2.8.0</kafka.version>
    <!-- After 10.15.1.3, the minimum required version is JDK9 -->
    <derby.version>10.14.2.0</derby.version>
    <parquet.version>1.12.1</parquet.version>
    <orc.version>1.6.11</orc.version>
    <jetty.version>9.4.43.v20210629</jetty.version>
    <jakartaservlet.version>4.0.3</jakartaservlet.version>
    <chill.version>0.10.0</chill.version>
    <ivy.version>2.5.0</ivy.version>
    <oro.version>2.0.8</oro.version>
    <!--
    If you changes codahale.metrics.version, you also need to change
    the link to metrics.dropwizard.io in docs/monitoring.md.
    -->
    <codahale.metrics.version>4.2.0</codahale.metrics.version>
    <avro.version>1.10.2</avro.version>
    <aws.kinesis.client.version>1.12.0</aws.kinesis.client.version>
    <!-- Should be consistent with Kinesis client dependency -->
    <aws.java.sdk.version>1.11.655</aws.java.sdk.version>
    <!-- the producer is used in tests -->
    <aws.kinesis.producer.version>0.12.8</aws.kinesis.producer.version>
    <!--  org.apache.httpcomponents/httpclient-->
    <commons.httpclient.version>4.5.13</commons.httpclient.version>
</properties>      

是以,我們的建構指令中​

​-D​

​要設定的如下

​mvn -Djava.version=11 -Dmaven.version=3.8.1 -Dhadoop.version=2.9.2 -Dscala.version=2.13.8 -DskipTests clean package​

接下來是​

​-P​

​參數

​-P​

​參數對應的是pom.xml中的profile屬性

<profile>
      <id>hadoop-2.7</id>
      <properties>
        <hadoop.version>2.7.4</hadoop.version>
        <curator.version>2.7.1</curator.version>
        <commons-io.version>2.4</commons-io.version>
        <hadoop-client-api.artifact>hadoop-client</hadoop-client-api.artifact>
        <hadoop-client-runtime.artifact>hadoop-yarn-api</hadoop-client-runtime.artifact>
        <hadoop-client-minicluster.artifact>hadoop-client</hadoop-client-minicluster.artifact>
      </properties>
    </profile>
    <profile>
      <id>hive-2.3</id>
    </profile>
    <profile>
      <id>yarn</id>
      <modules>
        <module>resource-managers/yarn</module>
        <module>common/network-yarn</module>
      </modules>
    </profile>
    <profile>
      <id>hive-thriftserver</id>
      <modules>
        <module>sql/hive-thriftserver</module>
      </modules>
    </profile>
    <profile>
      <id>scala-2.13</id>
      <properties>
        <scala.version>2.13.5</scala.version>
        <scala.binary.version>2.13</scala.binary.version>
      </properties>
      <build>
        <pluginManagement>
          <plugins>
            <plugin>
              <groupId>net.alchim31.maven</groupId>
              <artifactId>scala-maven-plugin</artifactId>
              <configuration>
                <args>
                  <arg>-unchecked</arg>
                  <arg>-deprecation</arg>
                  <arg>-feature</arg>
                  <arg>-explaintypes</arg>
                  <arg>-target:jvm-1.8</arg>
                  <arg>-Wconf:cat=deprecation:wv,any:e</arg>
                  <arg>-Wconf:cat=scaladoc:wv</arg>
                  <arg>-Wconf:cat=lint-multiarg-infix:wv</arg>
                  <arg>-Wconf:cat=other-nullary-override:wv</arg>
                  <arg>-Wconf:cat=other-match-analysis&site=org.apache.spark.sql.catalyst.catalog.SessionCatalog.lookupFunction.catalogFunction:wv</arg>
                  <arg>-Wconf:cat=other-pure-statement&site=org.apache.spark.streaming.util.FileBasedWriteAheadLog.readAll.readFile:wv</arg>
                  <arg>-Wconf:cat=other-pure-statement&site=org.apache.spark.scheduler.OutputCommitCoordinatorSuite.<local OutputCommitCoordinatorSuite>.futureAction:wv</arg>
                  <arg>-Wconf:msg=^(?=.*?method|value|type|object|trait|inheritance)(?=.*?deprecated)(?=.*?since 2.13).+$:s</arg>
                  <arg>-Wconf:msg=^(?=.*?Widening conversion from)(?=.*?is deprecated because it loses precision).+$:s</arg>
                  <arg>-Wconf:msg=Auto-application to \`\(\)\` is deprecated:s</arg>
                  <arg>-Wconf:msg=method with a single empty parameter list overrides method without any parameter list:s</arg>
                  <arg>-Wconf:msg=method without a parameter list overrides a method with a single empty one:s</arg>
                  <arg>-Wconf:cat=deprecation&msg=procedure syntax is deprecated:e</arg>
                </args>
                <compilerPlugins combine.self="override">
                </compilerPlugins>
              </configuration>
            </plugin>
          </plugins>
        </pluginManagement>
      </build>
    </profile>      

是以​

​-P​

​的參數大概就這麼多

​mvn -Phadoop-2.7 -Phive-2.3 -Pyarn -Phive-thriftserver -Pscala-2.13 -Djava.version=11 -Dmaven.version=3.8.4 -Dhadoop.version=2.9.2 -Dscala.version=2.13.8 -Dscala.binary.version=2.13 -DskipTests clean package​

編譯

如果使用的是scala-2.13版本,那麼需要切換scala版本

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

檢視​

​change-scala-version.sh​

​的内容,主要是環境變量的配置和pom.xml檔案中的版本号的修改。

在​

​pom.xml​

​​中,scala相關的配置是properties,理論上我們使用​

​-D​

​參數就能修改,不過可能有其他的配置,不一定是pom.xml的,反正執行一次最好。

而且,需要注意的是,執行這個指令,必須在spark主目錄下,因為在​

​change-scala-version.sh​

​腳本中使用相對路徑執行其他腳本,是以如果你在其他位置執行,那麼會因為上下文的問題,導緻執行失敗。

實際上,​

​change-scala-version.sh​

​​腳本還會使用spark主目錄下​

​build/mvn​

​程式。

我們使用上面拼接好的指令,在​

​SPARK_HOME​

​下執行

第一次執行,maven 會下載下傳相關的依賴,比較慢

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

哎,依賴是在是多,耐心等待吧

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

等待了一段時間,發現編譯錯誤了

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

很正常,一次成功才不正常。

我們檢視錯誤的堆棧,發現是因為找不到一個為​

​RangePartitioner​

​​的類,檢視源碼,這是一個在​

​apache/spark/blob/master/core/src/main/scala/org/apache/spark/Partitioner.scala​

​中定義的類

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

問題在于scala-2.13中沒有打包生成class檔案,而scala-2.12中就會打包生成calss檔案

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

生成的class檔案存儲在​

​/spark/core/target/scala-2.12/classes/org/apache/spark​

​目錄下

不知道是不是scala和spark的相容性問題。(我自己試了好多次,2.12.15可以編譯成功,2.13.8就編譯失敗了。)

在使用scala-2.12編譯的時候,編譯到spark-sql的時候,卡主,懷疑是記憶體不足了

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

幸好是虛拟機,可以增加記憶體,我們嘗試将記憶體增加到4G

然後終止編譯,重新編譯,并使用​

​-X​

​啟動debug輸出

經過嘗試,調大記憶體在一定程度上能加快編譯速度。

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

第一次編譯可以采用一步一步執行,可以先編譯後打包,其實打包也包含編譯,我單獨執行編譯是為了驗證scala-2.12是否會生成相關的class檔案,打包成功如下

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

編譯成功就會生成二進制檔案,存儲于spark根目錄的bin目錄下

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

其實這隻是一部分,這些腳本調用的jar包也會生成。

我們在源碼的bin目錄下就可以開心的使用spark了

直接啟動spark-shell

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

這些版本資訊和我們在編譯的時候指定的一模一樣。

編譯分發的二進制包

編譯好隻是這個環境編譯好了,裡面既有源碼檔案,也有可執行檔案,我們不能在叢集環境的每個節點上都進行編譯一次,那麼太過費時,而且也不可行。

是以我們需要編譯可分發的二進制包,就像官網提供的spark一樣,沒有源碼,全部是執行檔案。

在官網中是建議我們使用工具編譯分發的二進制包

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

檢視這個腳本,發現還是maven指令

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

最後将編譯後在bin目錄中的内容進行打包而已

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

我們在spark的根目錄下執行​

​./dev/make-distribution.sh --name 2.12 --tgz --mvn mvn -Phadoop-2.7 -Phive-2.3 -Pyarn -Phive-thriftserver -Pscala-2.12 -Djava.version=11 -Dmaven.version=3.8.4 -Dhadoop.version=2.9.2 -Dscala.version=2.12.15 -Dscala.binary.version=2.12 -DskipTests clean package​

打包完成後就會生成​

​psark-2.9.2-bin-2.12.tgz​

​壓縮包。

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

并不會列印maven的日志,等待就行了,可能需要和maven打包同樣長的時間。(2核,8G,大約40分鐘)

完成後會在spark根目錄下生成可分發二進制檔案包

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

實際上當我們使用maven指令完成打包後,隻是單獨的想打包分發二進制包,那麼是完全不需要重新執行maven打包的,畢竟maven打包太慢了。我們可以将spark根目錄下的​

​./dev/make-distribution.sh​

​檔案中的相關maven的指令跳過,注釋掉腳本中的這兩行即可。

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後把原來腳本中使用maven擷取版本号的方式自定義指定即可

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

這樣就不會重複執行maven指令了(maven指令太費時間了)

然後在maven打包完成的前提下,執行分發的二進制包的指令​

​./dev/make-distribution.sh --name 2.12 --tgz --mvn mvn -Phadoop-2.7 -Phive-2.3 -Pyarn -Phive-thriftserver -Pscala-2.12 -Djava.version=11 -Dmaven.version=3.8.4 -Dhadoop.version=2.9.2 -Dscala.version=2.12.15 -Dscala.binary.version=2.12 -DskipTests clean package​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

spark源碼編譯實際上就是maven編譯,如果你了解maven編譯,那麼在spark源碼編譯中遇到的問題,你都能很好的解決。

而二進制分發包的打包腳本,就是對maven編譯的使用,然後在把編譯後的檔案重新組合。

單機啟動

我們将編譯好的二進制包,從hadoop01分發到hadoop02上,使用​

​scp spark-3.2.0-bin-2.12.tgz hadoop02:/spark/​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後在hadoop02上解壓,​

​tar -zxvf spark-3.2.0-bin-2.12.tgz​

我們選擇在hadoop02上驗證二進制分發包是否可用。

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後将​

​SPARK_HOME​

​設定到環境變量,并使之生效

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後你可以在任意位置使用​

​spark-shell​

​啟動

相關的文檔在這裡​​Quick Start - Spark 3.2.0 Documentation (apache.org)​​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建
spark源碼編譯和叢集部署以及idea中sbt開發環境內建

啟動後如下

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

可見啟動spark并不需要scala環境,應該把scala的相關的包內建進去了

我們嘗試一下quick-started中的例子

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後打開​

​http://hadoop02:4040​

​檢視執行曆史

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

這種啟動方式,在啟動中會列印如下資料

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

這其實是spark的standalone的啟動方式

相關的文檔在這​​Overview - Spark 3.2.0 Documentation (apache.org)​​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

關于standalone的模式的相關文檔

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

​​Spark Standalone Mode - Spark 3.2.0 Documentation (apache.org)​​

叢集部署

為什麼直接啟動​

​spark-shell​

​就是單機版的模式呢?

spark的配置和hadoop差不多,有個slaves檔案,裡面會指定從節點的域名

預設的配置在​

​/spark/conf​

​檔案裡面

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

但是你在這裡面是找不到slaves檔案的,不過有一個workers.template檔案

檢視這個檔案,預設是localhost

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

在sbin目錄下有一個​

​slaves.sh​

​檔案

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

​slaves.sh​

​​檔案跳轉到了​

​workers.sh​

​檔案中了

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

通過這個腳本基本上就能明白了,spark的從節點配置在​

​/sbin/spark-config.sh​

​​和​

​/conf​

​中都可以配置

其中所有的配置都可以在​

​/conf/spark-env.sh​

​中配置

這些是一些standalone的配置

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

基本上都是通用的。

我們搭建一個spark的叢集,叢集資訊如下

節點 角色 其他
hadoop01 主節點 一個執行器
hadoop02 從節點 一個執行器
hadoop03 從節點 一個執行器

完整的​

​/conf/spark-env.sh​

​如下

# Options read when launching programs locally with
# ./bin/run-example or ./bin/spark-submit
# - HADOOP_CONF_DIR, to point Spark towards Hadoop configuration files
# - SPARK_LOCAL_IP, to set the IP address Spark binds to on this node
# - SPARK_PUBLIC_DNS, to set the public dns name of the driver program

# hadoop 的配置資訊
HADOOP_CONF_DIR=/hadoop/etc/hadoop

# Options read by executors and drivers running inside the cluster
# - SPARK_LOCAL_IP, to set the IP address Spark binds to on this node
# - SPARK_PUBLIC_DNS, to set the public DNS name of the driver program
# - SPARK_LOCAL_DIRS, storage directories to use on this node for shuffle and RDD data
# - MESOS_NATIVE_JAVA_LIBRARY, to point to your libmesos.so if you use Mesos

# spark shuffle的資料目錄,
SPARK_LOCAL_DIRS=/spark/shuffle_data

# Options read in YARN client/cluster mode
# - SPARK_CONF_DIR, Alternate conf dir. (Default: ${SPARK_HOME}/conf)
# - HADOOP_CONF_DIR, to point Spark towards Hadoop configuration files
# - YARN_CONF_DIR, to point Spark towards YARN configuration files when you use YARN
# - SPARK_EXECUTOR_CORES, Number of cores for the executors (Default: 1).
# - SPARK_EXECUTOR_MEMORY, Memory per Executor (e.g. 1000M, 2G) (Default: 1G)
# - SPARK_DRIVER_MEMORY, Memory for Driver (e.g. 1000M, 2G) (Default: 1G)

# yarn的配置檔案在hadoop的配置檔案中
YARN_CONF_DIR=$HADOOP_CONF_DIR
# 每個節點啟動幾個執行器
SPARK_EXECUTOR_CORES=1
# 每個執行器可以使用多大記憶體
SPARK_EXECUTOR_MEMORY=1800M
# 每個driver可以使用多大記憶體
SPARK_DRIVER_MEMORY=1800M

# Options for the daemons used in the standalone deploy mode
# - SPARK_MASTER_HOST, to bind the master to a different IP address or hostname
# - SPARK_MASTER_PORT / SPARK_MASTER_WEBUI_PORT, to use non-default ports for the master
# - SPARK_MASTER_OPTS, to set config properties only for the master (e.g. "-Dx=y")
# - SPARK_WORKER_CORES, to set the number of cores to use on this machine
# - SPARK_WORKER_MEMORY, to set how much total memory workers have to give executors (e.g. 1000m, 2g)
# - SPARK_WORKER_PORT / SPARK_WORKER_WEBUI_PORT, to use non-default ports for the worker
# - SPARK_WORKER_DIR, to set the working directory of worker processes
# - SPARK_WORKER_OPTS, to set config properties only for the worker (e.g. "-Dx=y")
# - SPARK_DAEMON_MEMORY, to allocate to the master, worker and history server themselves (default: 1g).
# - SPARK_HISTORY_OPTS, to set config properties only for the history server (e.g. "-Dx=y")
# - SPARK_SHUFFLE_OPTS, to set config properties only for the external shuffle service (e.g. "-Dx=y")
# - SPARK_DAEMON_JAVA_OPTS, to set config properties for all daemons (e.g. "-Dx=y")
# - SPARK_DAEMON_CLASSPATH, to set the classpath for all daemons
# - SPARK_PUBLIC_DNS, to set the public dns name of the master or workers

# 主節點
SPARK_MASTER_HOST=hadoop01
# 端口,不要和worker的端口重複,通信端口
SPARK_MASTER_PORT=4040
# 界面端口
SPARK_MASTER_WEBUI_PORT=8089
# 每個從節點啟動的執行器
SPARK_WORKER_CORES=1
# 每個從節點可使用最大記憶體
SPARK_WORKER_MEMORY=1800M
# 從節點的通信端口
SPARK_WORKER_PORT=4040
# 界面端口
SPARK_WORKER_WEBUI_PORT=8089
# 從節點工作目錄
SPARK_WORKER_DIR=/spark/worker

# Options for launcher
# - SPARK_LAUNCHER_OPTS, to set config properties and Java options for the launcher (e.g. "-Dx=y")

# Generic options for the daemons used in the standalone deploy mode
# - SPARK_CONF_DIR      Alternate conf dir. (Default: ${SPARK_HOME}/conf)
# - SPARK_LOG_DIR       Where log files are stored.  (Default: ${SPARK_HOME}/logs)
# - SPARK_LOG_MAX_FILES Max log files of Spark daemons can rotate to. Default is 5.
# - SPARK_PID_DIR       Where the pid file is stored. (Default: /tmp)
# - SPARK_IDENT_STRING  A string representing this instance of spark. (Default: $USER)
# - SPARK_NICENESS      The scheduling priority for daemons. (Default: 0)
# - SPARK_NO_DAEMONIZE  Run the proposed command in the foreground. It will not output a PID file.
# Options for native BLAS, like Intel MKL, OpenBLAS, and so on.
# You might get better performance to enable these options if using native BLAS (see SPARK-21305).
# - MKL_NUM_THREADS=1        Disable multi-threading of Intel MKL
# - OPENBLAS_NUM_THREADS=1   Disable multi-threading of OpenBLAS

# spark 的日志目錄
SPARK_LOG_DIR=/spark/logs      

相關的配置項說明在這​​Spark Standalone Mode - Spark 3.2.0 Documentation (apache.org)​​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後配置workers

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

接着把這個配置好的檔案分發到hadoop02的​

​/spark目錄下​

使用​

​scp -r /spark hadoop02:/spark/​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後在hadoop01上啟動(當然如果你在hadoop01上配置那麼就可以不需要從hadoop02分發到hadoop01上了)

别忘記​

​SPARK_HOME​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

接着切換到​

​/spark/sbin​

​目錄下,執行啟動腳本

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

啟動過程中提示hadoop03還沒有相關檔案

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

并且提示我們hadoop02的JAVA_HOME沒有設定,這就離譜了,我們第一個配置的就是JAVA_HOME。

錯誤資訊中給出了啟動worker的指令,我們直接拷貝到hadoop02上執行看下,發現是可以執行的

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

接着我們在界面中看下

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

到worker裡面看下,發現和我們配置的一樣

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

檢視java程序,發現master已經在hadoop01上啟動

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

hadoop02啟動的是worker

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

現在剩下一個問題,在hadoop01上啟動,無法啟動hadoop02的worker

我們檢視下​

​start-all.sh​

實際上​

​start-all.sh​

​​啟動了​

​start-master.sh​

​​和​

​start-worker.sh​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

重點看​

​start-worker.sh​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

可以看到會執行這兩個腳本,用于處理配置,我們把java環境在​

​spark-config.sh​

​中設定下

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後重新分發并重新開機

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

就沒有報錯,并且在hadoop02上成功啟動了

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

将​

​/spark​

​目錄分發到hadoop03并進行重新開機

spark源碼編譯和叢集部署以及idea中sbt開發環境內建
spark源碼編譯和叢集部署以及idea中sbt開發環境內建

需要注意,我們配置主節點和從節點的通信端口都是4040,界面通路端口都是8089。這也就是意味着,如果要在hadoop01上啟動Worker ,會出現端口沖突。

不過我們有三個節點,本來就是打算讓hadoop01成為master的。

開發環境內建

我們開發spark程式是在idea中開發的,是以需要在idea中安裝scala的插件

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後在windows環境中配置scala的環境

先把scala的檔案從hadoop01上下載下傳到windows中

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後配置環境變量

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

接着在idea中指定scala的sdk

spark源碼編譯和叢集部署以及idea中sbt開發環境內建
spark源碼編譯和叢集部署以及idea中sbt開發環境內建

idea會自動掃描全部的scala的sdk

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

選擇scala目錄

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

有了scala就可以建立spark的項目了,不過還需要scala的打包建構工具,也就是sbt

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

我們到​​sbt - The interactive build tool (scala-sbt.org)​​下載下傳sbt

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

下載下傳zip包就行

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

将下載下傳的sbt解壓到windows中,并配置環境變量

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

繼續建立項目(scala版本最好選擇和我們編譯spark相同的版本,當然建立好項目後在修改也是可以的)

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

根據上面這兩篇資料的内容,簡單了解下sbt。不希望能做到更好,隻要能正常使用就行了。

然後重新打開idea,打開建立的studyspark項目

sbt就開始下載下傳依賴了

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

此時sbt附加元件目會異常的,因為idea預設使用自己的sbt,而不是我們安裝的sbt

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

需要在settings中設定我們自己的sbt

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

重新建構項目

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

會彈出sbt-shell(如果你沒有把sbt配置的全部勾選,那麼應該是不會彈出sbt-shell的,那麼這步可以跳過)

​/*************************跳過開始*****************************​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

如果你也出現上述提示,那麼表示在等待sbt使用預設的配置,從國外下載下傳依賴

我們在sbt的配置中增加如下參數

-Dsbt.override.build.repos=true
-Dsbt.repository.config=E:\sbt\conf\repo.properties      

第一行表示使用全局的倉庫配置

第二行則是指定使用的倉庫配置的檔案路徑

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

記得取消這個勾選

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後重新導入項目

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

這裡發現sbt-shell中文亂碼了,很可惜,我嘗試了網上找到的方式,都失敗了,或許你可以試試,在sbt的參數中增加

-Dfile.encoding=UTF-8
-Dconsold.encoding=GBK      

對我都是無效的😆

​********************跳過結束*********************​

此時項目結構如下

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

項目相關的配置可以在​

​build.sbt​

​中修改

build.sbt如下

// 建立配置,用于辨別spark的版本
lazy val sparkVersion = SettingKey[String]("spark-version", "spark version")
// 定義公共的包基礎
lazy val packageBaseDir = SettingKey[String]("package-base-dir", "package base dir")
// 抽取公共配置
lazy val commonSettings = Seq(
  version := "1.0",
  scalaVersion := "2.12.15",
  sbtVersion := "1.6.1",
  sparkVersion := "3.2.0",
  // 定義項目包基礎
  packageBaseDir := "com.study.spark",
  // 定義每個項目的包字首
  ThisProject / idePackagePrefix := Some(packageBaseDir.value + "." + name.value),
  // 定義輸出目錄
  ideOutputDirectory := Some(file("target")),
  organization := "com.study.spark",
  // 申明源碼路徑
  sourceDirectories := Seq(
    file("src/main/scala"),
    file("src/main/java"),
    file("src/test/scala"),
    file("src/test/java")
  ),
  // 聲明資源路徑
  resourceDirectories := Seq(
    file("src/main/resources"),
    file("src/test/resources")
  ),
  libraryDependencies ++= Seq(
    "org.apache.spark" %% "spark-core" % sparkVersion.value % "provided",
    "org.apache.spark" %% "spark-sql" % sparkVersion.value % "provided",
    "org.apache.spark" %% "spark-mllib" % sparkVersion.value % "provided",
    "org.apache.spark" %% "spark-streaming" % sparkVersion.value % "provided"
  )
)
// 設定根項目
lazy val root = (project in file("."))
  .settings(
    name := "studyspark",
    commonSettings
  )      

接着我們把​

​sbt-assembly​

​插件加入到項目中

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

等待sbt重新整理項目後如下

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

因為我們在根項目中是不會進行任何編碼的,是以我們可以将根項目中的src目錄删除

最終我們的代碼是需要上傳到git的,是以建立​

​.gitignore​

​​檔案,将​

​target/​

​排除

我們建立一個helloworld的子產品,驗證sbt下的scala是否可用

spark源碼編譯和叢集部署以及idea中sbt開發環境內建
spark源碼編譯和叢集部署以及idea中sbt開發環境內建

别忘記在build.sbt中定義helloworld項目

lazy val helloworld = (project in file("helloworld"))
  .settings(
    name := "helloworld",
    commonSettings
  )      

别忘記建立目錄​

​main,test,scala,java,resources​

​等.

建立好後,我們建立如下代碼

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

在運作之前需要先執行compile操作

你可以在sbt-shell中執行

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

也可以在sbt工具視窗中執行(我比較喜歡在sbt-shell中操作)

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

點選運作

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

但是現在我們隻是能在ide中執行,我們的目的是打出jar包的,是以我們還需要配置​

​sbt-assembly​

​插件

打開​

​sbt-assembly​

​​插件的官方文檔​​sbt-assembly (scala-lang.org)​​

lazy val helloworld = (project in file("helloworld"))
  .settings(
    name := "helloworld",
    // 定義主類
  assembly / mainClass := Some(idePackagePrefix.value.get + ".Hello"),
    // 定義jar包名 項目名字_scala版本_項目版本.jar
    assembly / assemblyJarName := name.value + "_" + scalaVersion.value + "_" + version.value + ".jar",
    // 依賴合并政策
    assemblyMergeStrategy := {
      case PathList("javax", "servlet", xs @ _*)         => MergeStrategy.first
      case PathList(ps @ _*) if ps.last endsWith ".html" => MergeStrategy.first
      case "application.conf"                            => MergeStrategy.concat
      case "unwanted.txt"                                => MergeStrategy.discard
      case x =>
        val oldStrategy = (ThisBuild / assemblyMergeStrategy).value
        oldStrategy(x)
    },
    commonSettings
  )      

然後執行​

​assembly​

​操作,記得切換到helloworld項目下,你也可以在sbt工具視窗執行

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

不過我喜歡在sbt-shell中執行

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後耐心的等待吧,第一次會比較慢的

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

可以看到jar包已經生成了

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

我們使用java -jar 執行

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

可以看到打出來的jar包成功執行了

到了這裡scala和sbt都內建完畢,此時需要內建spark了

spark的依賴下載下傳會比較慢,而且新版本國内的鏡像不一定有,需要從國外下載下傳

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

這裡推薦一個Idea的插件:​

​Big Data Tools​

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

它可以連接配接到遠端的hadoop,hdfs,spark的叢集上,用于監控和互動,安裝後重新開機idea會在下面出現工具視窗

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

支援的還是挺多的

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

首先我們将插件和hadoop01進行連接配接

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

展示如下,和我們在浏覽器檢視的效果是一樣的

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

連接配接hdfs,需要注意,填寫的hdfs的通信位址,不是浏覽器位址

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

連接配接上之後,就可以像操作idea裡面的項目檔案一樣,上傳下載下傳等

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

接着我們啟動hadoop01的spark叢集

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

可惜了,連接配接spark還是有問題

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

到了這裡,工具裝了一大堆,但是還沒開始開發一行代碼,接下來就開發一個小案例wordcount試試吧

當我們安裝了​

​big data tools​

​​插件後,建立module的時候,就可以選擇​

​big data tools​

​​下的​

​spark​

​模闆了

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

這是一個類似于spring boot的模闆,裡面自動建立一些檔案和配置,更準确應該是和maven比較像

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

加載完畢後項目結構如下

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

不過這種建立項目隻是适合建立頂級項目,對于子級項目是不行的,會覆寫已有的build.sbt等資訊的

我們前面已經搭建好了頂級項目,是以我們可以直接建立一個scala項目就行了

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後指定子產品的名字就行了

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

我們做個優化,把打包的配置,除了主類之外的,都放在公共配置中

// 抽取公共配置
lazy val commonSettings = Seq(
  version := "1.0",
  scalaVersion := "2.12.15",
  sbtVersion := "1.6.1",
  sparkVersion := "3.2.0",
  // 定義項目包基礎
  packageBaseDir := "com.study.spark",
  // 定義每個項目的包字首
  ThisProject / idePackagePrefix := Some(packageBaseDir.value + "." + name.value),
  // 定義輸出目錄
  ideOutputDirectory := Some(file("target")),
  organization := "com.study.spark",
  // 申明源碼路徑
  sourceDirectories := Seq(
    file("src/main/scala"),
    file("src/main/java"),
    file("src/test/scala"),
    file("src/test/java")
  ),
  // 聲明資源路徑
  resourceDirectories := Seq(
    file("src/main/resources"),
    file("src/test/resources")
  ),
  libraryDependencies ++= Seq(
    "org.apache.spark" %% "spark-core" % sparkVersion.value % "provided",
    "org.apache.spark" %% "spark-sql" % sparkVersion.value % "provided",
    "org.apache.spark" %% "spark-mllib" % sparkVersion.value % "provided",
    "org.apache.spark" %% "spark-streaming" % sparkVersion.value % "provided"
  ),
  // 定義jar包名 項目名字_scala版本_項目版本.jar
  ThisProject / assembly / assemblyJarName := name.value + "_" + scalaVersion.value + "_" + version.value + ".jar",
  // 依賴合并政策
  ThisBuild / assemblyMergeStrategy := {
    case PathList("javax", "servlet", xs @ _*)         => MergeStrategy.first
    case PathList(ps @ _*) if ps.last endsWith ".html" => MergeStrategy.first
    case "application.conf"                            => MergeStrategy.concat
    case "unwanted.txt"                                => MergeStrategy.discard
    case x =>
      val oldStrategy = (ThisBuild / assemblyMergeStrategy).value
      oldStrategy(x)
  },
)      

然後把wordcount加入到根項目中,主類先不寫

lazy val wordcount = (project in file("wordcount"))
  .settings(
    name := "wordcount",
    commonSettings
  )      

然後建立目錄

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

我們在scala目錄下建立主類

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

首先使用本地進行執行

package com.study.spark.wordcount

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

object WordCount {

  def main(args: Array[String]): Unit = {
    // 配置spark
    val conf = new SparkConf().setMaster("local").setAppName("wordcount")
    // 擷取sparkContext
    val sc = new SparkContext(conf)
    // 從本地讀取檔案,其實就是 build.sbt 檔案
    sc.textFile("file:///E:\\java\\studyspark\\build.sbt")
      // 按照空格拆分
      .flatMap(_.split(" "))
      // 每個單詞計數1
      .map(_->1)
      // 按單詞分組統計
      .reduceByKey(_+_)
      // 計算
      .collect()
      // 列印統計結果
      .foreach(println(_))
  }

}      

别忘記在build.sbt中配置主類(之前我們隻有一個有效的項目,是以我們可以直接取參數的值,但是現在有了多個項目,就需要在取值的時候,限定作用域)

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

需要注意的是我們配置的spark依賴是不會在classpath中使用的,因為預設當我們把spark任務的jar包送出給spark叢集的時候,spark相關的依賴是不需要在jar包中的。

但是當我們需要本地啟動的時候,如果沒有spark的依賴,會導緻spark相關的類找不到。

是以我們需要在本地啟動的spark項目中,再次配置spark的依賴,差別是不寫後面​

​provided​

​作用域

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後切換到wordcount項目下,編譯并執行(使用run執行,而不是使用ide的執行按鈕)

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

如果一切順利,就會看到執行的日志

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

我們也可以打包的,使用​

​sbt-assembly​

​打包

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後使用java -jar 執行

執行會抛出異常,這是因為缺少了scala的依賴,我們需要把scala的依賴也加入

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

我們要求scala的依賴每次都必須打包,注意這裡是一個​

​%​

打出來的jar包不能簡單的使用java -jar 運作,需要送出到spark環境中執行,因為我們在打包的時候并沒有打spark相關的包。

為了能在伺服器上使用,我們需要修改我們的代碼,需要連結到伺服器上的spark叢集,而且讀取檔案應該讀取hdfs中的檔案

首先啟動伺服器上的hdfs服務

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

接着啟動spark叢集

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

接着把build.sbt上傳到hdfs中

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後重新打包,并上傳到伺服器

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後使用

spark-submit --master spark://hadoop01:4040 --class com.study.spark.wordcount.WordCount /spark/wordcount_2.12.15_1.0.jar      

送出到spark叢集,spark就開始執行了

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

執行結果和在本地執行一模一樣

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

而且在spark的界面中也能看到

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

如果你還沒有配置曆史服務,那麼會啟動報錯的,提示沒有配置曆史資料存儲路徑

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

拷貝spark-defaults.conf.template為spark-defaults.conf

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

内容如下

# 是否開啟日志
spark.eventLog.enabled true
# 日志存儲路徑
spark.eventLog.dir hdfs://hadoop01:8020/logs
# 是否壓縮
spark.eventLog.compress true      

同時在spark-env.sh中配置以下内容

# 配置spark曆史服務
SPARK_HISTORY_OPTS="-Dspark.history.ui.port=8086 -Dspark.history.retainedApplications=10 -Dspark.history.fs.logDirectory=hdfs://hadoop01:8020/logs"      

我們啟動spark曆史記錄服務

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

啟動曆史記錄服務不報錯

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

而且界面能打開

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

然後在執行一次,因為我們的任務很快,是以開啟曆史記錄,友善我們檢視job的資訊

這時候就能在曆史界面檢視了

spark源碼編譯和叢集部署以及idea中sbt開發環境內建
spark源碼編譯和叢集部署以及idea中sbt開發環境內建

我們實際上是想看看dag圖

spark源碼編譯和叢集部署以及idea中sbt開發環境內建

很漂亮。

源碼編譯的3.2.0版本無法在window上直接用spark-shell啟動

這個問題我發現不僅僅是我們自己編譯的二進制分發包有問題,就連官方下載下傳的二進制分發包,如果你直接使用​

​spark-shell​

​​啟動,也是會無法啟動的。這個問題困擾我了好幾天,我在網上找到了相關的讨論​​windows - Spark illegal character in path - Stack Overflow​​

但是也沒給出問題原因和解決方案,隻是說降低版本。

之前是源碼編譯二進制包,是以我也有spark的源碼,我自己根據堆棧找了好長時間,奈何自己水準太低,從源碼中沒有找到相關的代碼。

不過從我自己了解的源碼來看,目前好像就是​

​spark-shell​

​的repl的啟動存在問題,而送出作業等貌似是沒有問題的。(我自己沒試)

總結

整個文章的過程比較坎坷,比較艱難。

我是在看書的時候,知道了scala的sdk的二進制不相容,以及目前市面上大多數公司使用cdh的發行包,于是我也到cdh的官網想下載下傳最新的發行包,結果發現cdh版本貌似也開始收費了,于是隻能自己使用源碼編譯。

源碼編譯時第一個階段,當我曆經艱難,完成了源碼編譯後,發現不能在windows上使用spark-shell,于是在伺服器叢集上搭建spark叢集。這裡主要學習了spark的啟動和叢集部署,以及一些配置等。

上述這兩個階段基本上是年前完成的,在搭建spark開發環境的時候,才知道spark或者說scala推薦使用sbt編譯工具,可憐我maven都不是很熟悉,我哪會sbt呢。沒轍,想辦法研究sbt,研究sbt剛開始,就過年了,中間大概10多天吧,就是啥都沒做,新年開工,才開始正式研究sbt,研究幾天,基本上做到了入門,就繼續在idea中內建spark,sbt環境。

最終實作了idea本地開發編碼,本地在sbt的基礎上執行,然後打包送出到spark叢集中執行。

繼續閱讀