spark-submit 到底做了什麼

本文的目的是提升linux shell腳本的功力,以及熟悉spark-submit提交的具體流程

spark-sumbit*

#!/usr/bin/env bash

if [ -z "${SPARK_HOME}" ]; then
  source "$(dirname "$0")"/find-spark-home
fi

# disable randomized hash for string in Python 3.3+
export PYTHONHASHSEED=0

exec "${SPARK_HOME}"/bin/spark-class org.apache.spark.deploy.SparkSubmit "$@"

第一段

if [ -z "${SPARK_HOME}" ]; then
  source "$(dirname "$0")"/find-spark-home
fi

背景知識

  1. 雙引號

    "${SPARK_HOME}"${SPARK_HOME} 這樣寫是等效的,均代表獲取環境變量中的SPARK_HOME所引用的值
    在shell中 “$USER” 與 $USER 其代表的值是一樣的。 加不加並不會記錄到變量中,也不會添加到字符串中,其代表的一個根本的含義就是 定義 變量的意思。

    默認情況下,在shell編程中,不帶雙引號的都會被當作字符串來處理。這個慣例比較特殊。反正與一般意義上的高級編程語言不太一致。(讀者可以自己揣摩一下單引號會得出什麼樣的結論)

    #!/bin/bash
    if [ $USER = "$USER" ];then
            echo "eq" # 執行eq
    else
            echo "neq"
    fi
    
    
  2. if(空格)[(空格)-[a-z](空格)(string)(空格)];then 語法

    這個是常見的shell 字符串條件判斷的格式,-z代表不存在,請注意空格是必填的,這個是跟處理shell腳本的編譯器設置的語法解析器有關,或者說內置的正則表達式就是這麼獲取條件匹配的
    在方括號內退出碼爲0(exit 0即正常退出)則執行then後面的語句,否則執行其他分支

    homewell:/home/hadoop/shareh$ cat /etc/passwd | grep abc
    homewell:/home/hadoop/shareh$ echo $?
    1
    

    比較其原型 是test命令

  3. source命令(直接copy命令)

    有人說是點(.)命令,也就是執行目標文件中的命令,但是source 不會執行生成子shell(概念要理清,這個子shell代表這創建一個新的進程被父進程管理環境變量)

    如果讀者學過C++或者C,都會接觸宏(#define)關鍵字,代表者代碼在編譯的時候直接把代碼copy到引用了宏定義的代碼段中,或者類似於C++中的內聯函數,都是代碼的直接copy.可以看 綜合案例

    因此可以使用另一個腳本中暴露(export)的變量,因爲相當於把另一腳本中的代碼,直接copy到當前腳本中

    參考1
    參考2

  4. dirname 命令

    獲取指定虛擬目錄的父目錄

    homewell:/home/hadoop/shareh$ dirname /home/hadoop/shareh/
    /home/hadoop
    homewell:/home/hadoop/shareh$ dirname /home/hadoop/shareh/abc
    abc/    abcd/   abc.sh  
    homewell:/home/hadoop/shareh$ dirname /home/hadoop/shareh/abc.sh 
    /home/hadoop/shareh
    
  5. 位置參數

    $0是調用程序的時候第一個字符串,通常與程序名一致,往往有些博客簡單地把它理解成程序名,這樣理解是很受傷的,很容易誤傷,針對這種情況。希望讀者能區分不同場景來理解(查看本案例)

    $1是第一個參數,$2是第二個參數

    5.案例

        #!/bin/bash
    
        echo "\$0:""$0" 
        echo "\$1:""$1"
        # 超過10個參數的用{}包裹起來,否則 $10 == "$1" + "0"
        echo "第十個參數""${10}"
    
        # 第一 全路徑方式
        homewell:~$ /home/homewell/shell1/testdoller.sh 23
        $0:/home/homewell/shell1/testdoller.sh
        $1:23
        
        # 全局變量方式(同上是一致的)
        homewell:~/shell1$ export SHELLTEST_HOME="/home/homewell/shell1"
        homewell:~/shell1$ export PATH="$SHELLTEST_HOME:$PATH"
        homewell:~/shell1$ testdoller.sh 1235
        $0:/home/homewell/shell1/testdoller.sh
        $1:1235
        
        # 第二種 通過相對路徑1
        homewell:~$ shell1/testdoller.sh 124
        $0:shell1/testdoller.sh
        $1:124
        
        # 第二種 通過相對路徑2
        homewell:~$ ./shell1/testdoller.sh 124
        $0:./shell1/testdoller.sh
        $1:124
        
        # 第三種 直接以./方式執行
        homewell:~$ cd shell1/
        homewell:~/shell1$ ./testdoller.sh 125
        $0:./testdoller.sh
        $1:125
        
        # 第四種 通過sh 命令執行
        homewell:~/shell1$ sh testdoller.sh 126
        $0:testdoller.sh
        $1:126
    
    
    

擴展內容 其他內置的跟$有關的位置參數

綜合案例

-rwxr-xr-x  1 zhangll zhangll  184 10月  2 21:14 pid2.sh*
-rwxrwxrwx  1 zhangll zhangll 1292 10月  2 21:40 pid.sh*

pid.sh

#!/bin/bash
表示當前shell
echo "pid.sh當前shell pid進程(\$$):$$"
echo "pid.sh當前shell最近一個後臺進程pid(\$!):" "$!"
echo "pid.sh前面一個命令的退出碼 (\$?) : $?"



# 執行當前的目錄路徑下的pid2.sh
echo "-------------./pid2.sh--------------"
./pid2.sh
echo "pid.sh當前進程打印 pid2中的pid2暴露的變量(\$PID2): $PID2"


echo "-------------查看(子進程)的一些特性------------"
# 向外暴露a變量,在父進程中無法查看,只有在子shell中能看
export a=1
echo "pid.sh當前shell顯示a 變量(\$a):"$a
(export b=2; echo "子進程查看父進程暴露的(a):""$a";echo "子進程暴露的變量(\$b):""$b";echo "子進程打印的進程(\$\$):""$$";echo "子進程(\$BASHPID):$BASHPID")
echo "pid.sh 在 (子進程之後)當前shell最近一個後臺進程pid2(\$!):" "$!"
echo "pid.sh當前shell顯示子進程暴露的b變量(\$b) : $b"



echo "------------(./pid2.sh &)------------"
./pid2.sh &
echo "pid.sh 在 (./pid2.sh &)最近一個後臺進程pid2(\$!):" "$!"
echo "pid.sh當前進程打印 pid2中的pid2暴露的變量(\$PID2): $PID2"




echo "-----------source pid2.sh -----------"
source ./pid2.sh
echo "pid.sh source pid2.sh 之後當前進程打印 pid2中的pid2暴露的變量(\$PID2): $PID2"


exit 0


pid2.sh

#!/bin/bash
echo "######pid2.sh 打印的 (\$\$):$$"
echo "######pid2.sh 打印的 (\$BASHPID):$BASHPID"
echo "######pid2.sh 打印的最近一個後臺進程 (\$!):$!"
export PID2=30

執行結果

pid.sh當前shell pid進程($$):9553
pid.sh當前shell最近一個後臺進程pid($!): 
pid.sh前面一個命令的退出碼 ($?) : 0


-------------./pid2.sh--------------
######pid2.sh 打印的 ($$):9554
######pid2.sh 打印的 ($BASHPID):9554
######pid2.sh 打印的最近一個後臺進程 ($!):
pid.sh當前進程打印 pid2中的pid2暴露的變量($PID2): 


-------------查看(子進程)的一些特性------------
pid.sh當前shell顯示a 變量($a):1
子進程查看父進程暴露的(a):1
子進程暴露的變量($b):2
子進程打印的進程($$):9553
子進程($BASHPID):9555
pid.sh 在 (子進程之後)當前shell最近一個後臺進程pid2($!): 
pid.sh當前shell顯示子進程暴露的b變量($b) : 


------------(./pid2.sh &)------------
######pid2.sh 打印的 ($$):9556
######pid2.sh 打印的 ($BASHPID):9556
######pid2.sh 打印的最近一個後臺進程 ($!):
pid.sh 在 (./pid2.sh &)最近一個後臺進程pid2($!): 9556
pid.sh當前進程打印 pid2中的pid2暴露的變量($PID2): 


-----------source pid2.sh -----------
######pid2.sh 打印的 ($$):9553
######pid2.sh 打印的 ($BASHPID):9553
######pid2.sh 打印的最近一個後臺進程 ($!):9556
pid.sh source pid2.sh 之後當前進程打印 pid2中的pid2暴露的變量($PID2): 30

可以發現

  1. $! 代表當前shell進程空間最近一個執行的後臺進程的id,並不會獲取父進程空間的最近一次後臺進程pid
    後臺進程與子進程的區別
  2. ()括號包裹的雖然是開啓了一個新的空間,但是其進程id與父親的一致,但是在其內暴露(export)的變量,父進程無法獲取
  3. $$ 並非代表當前shell的pid,

解讀

不管以./spark-submit 還是 直接執行spark-submit,當不存在SPARK_HOME環境變量的時候,就執行當前目錄(spark-submit所在目錄)中的find-spark-home命令
很顯然find-spark-home 要做的工作肯定是設置一個 SPARK_HOME 環境變量用來給後期使用

第二段

# disable randomized hash for string in Python 3.3+
export PYTHONHASHSEED=0

背景知識

  1. export 暴露/導出

    該關鍵詞在nodejs/js/c/c++中會經常出現,代表這給調用該腳本的環境(腳本/shell環境)暴露一個變量,這個變量將會在當前進程空間中使用,包括子進程空間,但是不會影響父進程空間的變量

解讀

很顯然暴露了一個PYTHONHASHSEED (python hash seed)
這個是設置一個python隨機
中的seed值,當改值爲0的時候,代表着關閉隨機hash種子,意味着對相同字符串多次使用hash函數,得到的是相同的值,這裏可能考慮到pyspark上會調用hash值產生不必要的麻煩,比如spark中的group by 算子,由於可能數據在不同節點上,因此不同節點上會啓動不同的python環境(可以理解成python session),python3.2.3之後就默認開啓python環境的hash值,這意味着不同環境下的相同字符串,會默認在字符串前面添加一個鹽值,防止DOS拒絕服務攻擊手段,防止鏈表太長,降低性能。然後在group by 的相同字符串可能會在不同機器上輸出不同 內容,因此爲了能夠保證數據一致性,需要關閉這個值

地址

第三段

exec "${SPARK_HOME}"/bin/spark-class org.apache.spark.deploy.SparkSubmit "$@"

背景知識

  1. exec 命令 參數

    代表當前命令與參數 直接覆蓋當前shell內容,這個是最暴力宏替換,不過會暴露當前的用戶變量(局部變量)與全局變量

    案例見 Linux shell 整理之 複合命令行篇(四)

  2. exec 永久重定向操作

    案例見 Linux shell 整理之 複合命令行篇(四)

  3. 雙引號

    同第一段 1)

解讀

因此第三段的意思就是使用spark_home/bin/spark-class 並且傳遞兩個參數
第一個是 org.apache.spark.deploy.SparkSubmit
第二個是 "@"shell"@" 代表當前shell 執行所竄入的參數個體集合的列表,根據實際情況當然可以用 ""來代替。$@ 代表着把輸入參數作爲多個個體存在,而 $ 可以作爲一個單獨的個體存在

總結語

紙上得來終覺淺,絕知此事要躬行。簡簡單單3句話,其涵蓋的內容,不是一兩句話能夠得出來的。當你用心去體會的時候,你會發現,原來一切都這麼簡單明白

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章