Spark權威指南(中文版)----第16章 開發Spark應用程序

Spark The Definitive Guide(Spark權威指南) 中文版。本書詳細介紹了Spark2.x版本的各個模塊,目前市面上最好的Spark2.x學習書籍!!!

掃碼關注公衆號:登峯大數據,閱讀中文Spark權威指南(完整版),系統學習Spark大數據框架!

如果您覺得作者翻譯的內容有幫助,請分享給更多人。您的分享,是作者翻譯的動力

在第15章中,您瞭解了Spark如何在集羣上運行代碼。現在,我們將向您展示開發一個獨立的Spark應用程序並將其部署到集羣上是多麼容易。我們將使用一個簡單的模板來實現這一點,該模板分享了一些關於如何構建應用程序的簡單技巧,包括設置構建工具和單元測試。這個模板可以在本書的代碼存儲庫中找到。這個模板實際上並不是必需的,因爲從頭編寫應用程序並不困難,但是它很有幫助。讓我們從第一個應用程序開始。

16.1.   編寫Spark應用程序

Spark應用程序是兩種東西的組合:Spark集羣和代碼。在這種情況下,集羣將是本地模式,應用程序將是預定義的模式。讓我們瀏覽一下每種語言中的應用程序。

16.1.1.   一個簡單的基於scala的應用程序

Scala是Spark的“原生”語言,自然是編寫應用程序的好方法。這和編寫Scala應用程序沒什麼不同。

提示Scala看起來有些嚇人,這取決於您的背景,但如果只是爲了更好地理解Spark,那麼還是值得學習的。此外,你不需要學習這門語言的所有細節;從基礎開始,您會發現在Scala中很快就可以提高生產力。使用Scala還將打開許多大門。通過一點實踐,通過Spark的代碼庫進行代碼級跟蹤並不困難。

您可以使用兩個基於Java虛擬機(JVM)的構建工具sbt或Apache Maven構建應用程序。與任何構建工具一樣,它們都有各自的怪癖,但是從sbt開始可能是最簡單的。您可以在sbt網站上下載、安裝和了解sbt。您也可以從Maven各自的網站上安裝Maven。

要爲Scala應用程序配置sbt build,我們需要指定一個build.sbt文件,用於管理包信息。在build.sbt文件中,有幾個關鍵的地方:

  • 項目元數據(包名稱、包版本控制信息等)

  • 在哪裏解決依賴關係

  • 庫所需的依賴項

您可以指定更多的選項;但是,它們超出了本書的範圍(您可以在web和sbt文檔中找到相關信息)。也有一些關於這個主題的書籍,可以作爲一個有用的參考。下面是Scala build.sbt文件的示例(以及我們在模板中包含的文件)。注意,我們必須指定Scala版本和Spark版本:

現在我們已經定義了構建文件,實際上可以開始向項目添加代碼了。我們將使用標準的Scala項目結構,你可以在sbt參考手冊中找到(這是與Maven項目相同的目錄結構):

 

我們將源代碼放在Scala和Java目錄中。在本例中,我們將如下內容放入文件中;這將初始化SparkSession,運行應用程序,然後退出:

注意,我們定義了一個包括main方法的類,當使用spark-submit將其提交到集羣執行時,可以從命令行運行這個類。

現在我們已經設置好了我們的項目並向其添加了一些代碼,是時候構建它了。我們可以使用sbt assemble構建一個“超級JAR”或“胖JAR”,其中包含一個JAR中的所有依賴項。對於某些部署,這可能很簡單,但對於其他部署,這可能會導致複雜性(尤其是依賴衝突)。一個輕量級的方法是運行sbt package,它將把所有依賴項收集到目標文件夾中,但不會將所有依賴項打包到一個大JAR中。

運行程序

目標文件夾包含我們可以用作spark-submit參數的JAR。在構建Scala包之後,您可以使用以下代碼在本地機器上進行spark-submit(此代碼片段利用別名創建$SPARK_HOME變量;你可以將$SPARK_HOME替換爲包含你下載的Spark版本的目錄):

16.1.2.   編寫Python應用程序

編寫PySpark應用程序實際上與編寫普通的Python應用程序或包沒有什麼不同。它特別類似於編寫命令行應用程序。Spark沒有構建概念,只有Python腳本,因此要運行應用程序,只需對集羣執行腳本。

爲了促進代碼重用,通常將多個Python文件打包到Spark代碼的egg或ZIP文件中。要包含這些文件,您可以使用spark-submit的——py-files參數來添加.py、.zip或.egg文件,以便與應用程序一起分發。

當運行代碼時,用Python創建一個等價於“Scala/Java main類”的類。將某個腳本指定爲構建SparkSession的可執行腳本。這是我們傳遞給spark-submit的主要參數:

當您這樣做時,您將獲得一個SparkSession,您可以將它傳遞給您的應用程序。最佳實踐是在運行時傳遞這個變量,而不是在每個Python類中實例化它。

在Python中開發時,一個有用的技巧是使用pip將PySpark指定爲依賴項。您可以通過運行命令pip install pyspark來實現這一點。這允許您以可能使用其他Python包的方式使用它。這也使得許多編輯器中的代碼完成非常有用。這是Spark 2.2中全新的一個版本,因此可能需要一兩個版本才能完全投入生產,但是Python在Spark社區中非常流行,它肯定是Spark未來的基石。

運行程序

編寫完代碼之後,就可以提交代碼執行了。(我們正在執行與項目模板中相同的代碼。)您只需要調用spark-submit與該信息:

16.1.3.   編寫Java應用程序

編寫Java Spark應用程序與編寫Scala應用程序是一樣的。核心差異涉及到如何指定依賴項。

本例假設您正在使用Maven指定依賴項。在本例中,您將使用以下格式。在Maven中,您必須添加Spark Packages存儲庫,以便從這些位置獲取依賴項:

當然,您遵循與Scala項目版本相同的目錄結構(因爲它們都符合Maven規範)。然後,我們只需遵循相關的Java示例來實際構建和執行代碼。現在,我們可以創建一個簡單的例子,指定一個main類,讓我們執行(更多關於這個在本章末尾):

然後,我們使用mvn package對它進行打包(需要安裝Maven)。

運行程序

這個操作將與運行Scala應用程序(或者Python應用程序)完全相同。簡單地使用spark-submit:

16.2.    測試Spark應用程序

現在您已經知道編寫和運行一個Spark應用程序需要什麼,所以讓我們轉到一個不那麼令人興奮但仍然非常重要的主題:測試。測試Spark應用程序依賴於幾個關鍵原則和策略,在編寫應用程序時應該牢記這些原則和策略。

16.2.1.   策略原則

測試數據pipelines和Spark應用程序與實際編寫它們一樣重要。這是因爲您希望確保它們對未來的數據、邏輯和輸出方面的更改具有彈性。在本節中,我們將首先討論您可能希望在典型的Spark應用程序中測試什麼,然後討論如何組織代碼以便進行簡單的測試。

輸入數據的彈性

對不同類型的輸入數據保持彈性對於如何編寫數據管道非常重要。數據將會改變,因爲業務需求將會改變。因此,您的Spark應用程序和管道應該至少對輸入數據中的某種程度的更改具有彈性,或者確保以一種優雅而有彈性的方式處理這些故障。在大多數情況下,這意味着要聰明地編寫測試來處理不同輸入的邊緣情況。

業務邏輯彈性和演化

管道中的業務邏輯和輸入數據都可能發生更改。更重要的是,您希望確保從原始數據中推導出來的是您實際認爲自己在推導的東西。這意味着您將需要對實際數據進行健壯的邏輯測試,以確保您實際上得到了您想要的結果。這裏需要注意的一件事是,嘗試編寫一組“Spark單元測試”,只測試Spark的功能。你可能不想這樣做;相反,您希望測試您的業務邏輯,並確保您設置的複雜業務管道實際上正在做您認爲它應該做的事情。

輸出的彈性和原子性

假設您已經爲輸入數據結構中的更改做好了準備,並且您的業務邏輯經過了良好的測試,現在您需要確保您的輸出結構是您所期望的。這意味着您需要優雅地處理輸出模式解析。通常情況下,數據不會被簡單地轉儲到某個位置,再也不會被讀取—您的大多數Spark管道可能正在爲其他Spark管道提供數據。因爲這個原因你要確保下游消費者理解的“狀態”,可能意味着它更新的頻率,以及數據是否“完整的”(例如,沒有後期數據),或者不會有任何最後一分鐘修正數據。

前面提到的所有問題都是構建數據管道時應該考慮的原則(實際上,無論是否使用Spark)。這種戰略思維對於爲您想要構建的系統打下基礎非常重要。

16.2.2.   戰術

雖然戰略思維很重要,但是讓我們更詳細地討論一些可以使應用程序易於測試的策略。最高價值的方法是通過使用適當的單元測試來驗證您的業務邏輯是正確的,並確保您對不斷變化的輸入數據具有彈性,或者已經對其進行了結構化,以便將來模式演化不會失去作用。如何做到這一點,很大程度上取決於作爲開發人員的您,因爲這將根據您的業務領域和領域專長而有所不同。

管理SparkSessions

使用單元測試框架(如JUnit或ScalaTest)測試Spark代碼相對容易,因爲Spark具有本地模式——只需創建一個本地模式SparkSession作爲測試工具的一部分來運行它。然而,要使此工作正常,您應該在管理代碼中的Spark時儘可能多地執行依賴項注入。也就是說,只初始化SparkSession一次,並在運行時將其傳遞給相關的函數和類,以便在測試期間方便地進行替換。這使得在單元測試中使用一個虛擬的SparkSession測試每個單獨的函數更加容易。

使用哪個Spark API ?

Spark提供了多種api的選擇,從SQL到DataFrames和Datasets,每一種api都可能對應用程序的可維護性和可測試性產生不同的影響。坦白地說,正確的API取決於您的團隊及其需求:一些團隊和項目將需要不那麼嚴格的SQL和DataFrame API來提高開發速度,而其他團隊則希望使用類型安全的數據集或RDDs。

通常,我們建議對每個函數的輸入和輸出類型進行文檔化和測試,而不管使用哪種API。類型安全API自動爲您的函數強制執行一個最小的契約,這使得其他代碼很容易在此基礎上進行構建。如果您的團隊更喜歡使用DataFrames或SQL,那麼請花一些時間記錄和測試每個函數返回什麼,以及它接受什麼類型的輸入,以避免以後出現意外,就像在任何動態類型的編程語言中一樣。雖然較低層的RDD API也是靜態類型的,但我們建議只在需要數據集中不存在的底層特性(比如分區)時才使用它,這應該不是很常見;Dataset API允許更多的性能優化,並且將來可能提供更多的性能優化。

對於應用程序使用哪種編程語言也有類似的考慮:對於每個團隊當然沒有正確的答案,但是根據您的需要,每種語言將提供不同的好處。我們一般建議使用靜態類型語言,像Scala和Java爲更大的應用程序或者那些你希望能夠進入低級代碼完全控制性能,但Python和R可能是更好的在其他情況下,示例中,如果您需要使用一些其他的庫。Spark代碼應該很容易在每種語言的標準單元測試框架中進行測試。

連接到單元測試框架

要對代碼進行單元測試,我們建議使用語言中的標準框架(例如JUnit或ScalaTest),並設置測試工具來爲每個測試創建和清理SparkSession。不同的框架提供了不同的機制來實現這一點,例如“before”和“after”方法。我們在本章的應用程序模板中包含了一些單元測試代碼示例。

連接到數據源

儘可能地,您應該確保您的測試代碼不連接到生產數據源,這樣,如果這些數據源發生更改,開發人員就可以輕鬆地單獨運行它。實現這一點的一個簡單方法是讓所有業務邏輯函數都以DataFrames或數據集作爲輸入,而不是直接連接到各個源;畢竟,無論數據源是什麼,後續代碼都將以相同的方式工作。如果您在Spark中使用結構化api,實現這一點的另一種方法是命名錶:您可以簡單地註冊一些虛擬數據集(例如,從小文本文件或內存對象加載)作爲各種表名,然後從那裏開始。

16.3.  開發過程

使用Spark應用程序的開發過程類似於您可能已經使用過的開發工作流。首先,您可能要維護一個劃痕空間,比如交互式筆記本或其他類似的東西,然後在構建關鍵組件和算法時,將它們移動到更持久的位置,比如庫或包。筆記本體驗是我們經常推薦的體驗之一(我們也經常用它來寫這本書),因爲它在實驗中很簡單。還有一些工具,比如Databricks,允許您將筆記本作爲生產應用程序運行。

在本地機器上運行時,spark-shell及其各種特定於語言的實現可能是開發應用程序的最佳方法。在大多數情況下,shell用於交互式應用程序,而Spark -submit用於Spark集羣上的生產應用程序。您可以使用shell以交互方式運行Spark,就像我們在本書開頭介紹的那樣。這是運行PySpark、Spark SQL和SparkR的模式。在bin文件夾中,當您下載Spark時,您將找到啓動這些shell的各種方法。只需運行spark-shell(對於Scala)、spark-sql、pyspark和sparkR。

在您完成應用程序並創建要運行的包或腳本之後,spark-submit將成爲您向集羣提交此作業的最好朋友。

16.4.  運行程序

運行Spark應用程序的最常見方法是通過Spark -submit。在本章前面,我們向您展示瞭如何運行spark-submit;您只需指定選項、應用程序JAR或腳本以及相關參數:

在使用Spark -submit提交Spark作業時,始終可以指定是在客戶機模式還是在集羣模式下運行。但是,您應該幾乎總是傾向於在集羣模式下運行(或者在集羣本身的客戶機模式下),以減少執行者和驅動程序之間的延遲。

提交applciations時,在.jar中傳遞一個.py文件,並將Python .zip、.egg或.py添加到搜索路徑中,其中包含—py文件。

爲了便於參考,表16-1列出了所有可用的spark-submit選項,包括一些集羣管理器特有的選項。要自己列舉所有這些選項,請運行spark-submit with——help。

 還有一些特定於部署的配置(參見表16-2)。

16.4.1.   應用程序運行的例子

在本章之前,我們已經介紹了一些本地模式的應用程序示例,但是值得一看的是我們如何使用前面提到的一些選項。Spark還在下載Spark時包含的examples目錄中包含幾個示例和演示應用程序。如果你糾結於如何使用某些參數,你可以先在本地機器上試試,然後使用SparkPi類作爲主類:

下面的代碼片段對Python也做了同樣的操作。您可以從Spark目錄運行它,這將允許您向獨立集羣管理器提交一個Python應用程序(都在一個腳本中)。您還可以設置與前一個示例相同的執行器限制:

16.5.  配置應用程序

Spark包含許多不同的配置,其中一些我們在第15章中已經介紹過。根據您希望實現的目標,有許多不同的配置。本節將詳細介紹這些內容。大多數情況下,這些信息都是供參考的,可能只值得略讀,除非您正在尋找特定的內容。大多數配置可分爲以下幾類:

Spark提供了三個位置來配置

  • Spark屬性控制大多數應用程序參數,可以通過使用SparkConf對象來設置

  • Java系統屬性

  • 硬編碼的配置文件

 您可以使用幾個模板,您可以在Spark home文件夾的根目錄中找到/conf目錄。您可以將這些屬性設置爲應用程序中的硬編碼變量,或者在運行時指定它們。您可以使用環境變量在每個節點上通過conf/spark-env.sh腳本設置每臺機器的設置,例如IP地址。最後,您可以通過log4j.properties配置日誌記錄。

16.5.1.   SparkConf

SparkConf管理所有應用程序配置。您可以通過import語句創建一個,如下面的示例所示。創建之後,SparkConf對於特定的Spark應用程序是不可變的:

您可以使用SparkConf配置具有Spark屬性的單個Spark應用程序。這些Spark屬性控制Spark應用程序的運行方式和集羣的配置方式。下面的示例將本地集羣配置爲有兩個線程,並指定在Spark UI中顯示的應用程序名稱。

您可以在運行時配置它們,正如您在本章前面通過命令行參數看到的那樣。這有助於啓動一個Spark Shell,它將自動爲您包含一個基本的Spark應用程序;例如:

值得注意的是,在設置基於時間期限的屬性時,應該使用以下格式:

16.5.2.   應用程序屬性

應用程序屬性是您從Spark -submit或創建Spark應用程序時設置的屬性。它們定義了基本的應用程序元數據以及一些執行特性。表16-3給出了當前應用程序屬性的列表。

通過應用程序web UI(Driver程序端口4040)的”Environment”選項卡,查看所有的配置信息,可以確保正確設置了這些值。只有通過spark-defaults、SparkConf或命令行顯式設置的值纔會出現在選項卡中。對於所有其他配置屬性,可以假設使用默認值。

16.5.3.   運行環境屬性配置

雖然不太常見,但有時可能還需要配置應用程序的運行時環境。由於空間限制,我們不能在這裏包含整個配置集。請參考Spark文檔中有關運行時環境的相關配置表(http://spark.apache.org/docs/latest/configuration.html#runtime-environment)。這些屬性允許您爲Driver程序和Executor程序配置額外的類路徑和python路徑、python相關員配置以及其他日誌記錄配置屬性。

16.5.4.   執行時屬性配置

這些配置是您需要配置的最相關的配置之一,因爲它們爲您提供了對程序實際執行的更細粒度控制。由於空間限制,我們不能在這裏包含整個配置集。請參考Spark文檔中有關執行行爲的相關配置表(http://spark.apache.org/docs/latest/configuration.html#execution-behavior)。最常見的更改配置是spark.executor.cores(控制可用cores的數量)和spark.files.maxPartitionBytes(讀取文件時的最大分區大小)。

16.5.5.   配置內存管理

有時您可能需要手動管理內存選項來嘗試優化應用程序。其中許多配置項與最終用戶無關,因爲它們涉及許多遺留概念或在Spark 2中因自動內存管理而被排除的細粒度控制配置項。由於空間限制,我們不能在這裏包含整個配置集。請參閱Spark文檔中有關內存管理的相關配置表(http://spark.apache.org/docs/latest/configuration.html#memory-management)。

16.5.6.   配置shuffle行爲

我們已經強調過,由於Spark作業的通信開銷很高,所以shuffle可能會成爲瓶頸。因此,有許多低層配置用於控制shuffle行爲。由於空間限制,我們不能在這裏包含整個配置集。請參考Spark文檔中有關Shuffle行爲的相關配置表(http://spark.apache.org/docs/latest/configuration.html#shuffle-behavior)。

16.5.7.   環境變量

您可以通過環境變量配置某些Spark設置,這些環境變量來自安裝Spark的目錄中的conf/ Spark-env.sh腳本。在Standalone模式和Mesos模式中,該文件可以提供特定於機器的信息,比如主機名。當運行本地Spark應用程序或提交腳本時,它也會被引用。

注意,默認情況下,安裝Spark時不存在conf/ Spark-env.sh文件,可以通過拷貝conf/spark-env.sh.template並重命名獲得。

以下變量可以在spark-env.sh中設置:

JAVA-HOME

安裝Java的位置(如果它不在默認路徑上)。

PYSPARK_PYTHON

Python二進制可執行文件,用於Driver程序和Worker程序中的PySpark(如果可用,默認爲python2.7;否則,python)。如果spark.pyspark.python屬性設置了,則spark.pyspark.python屬性優先級高於此環境變量。

PYSPARK_DRIVER_PYTHON

Python二進制可執行文件,僅用於Driver程序中的PySpark(默認爲PYSPARK_PYTHON)。屬性spark.pyspark.driver.python如果設置了,則它優先。

SPARKR_DRIVER_R

用於SparkR shell的二進制可執行文件(默認爲R)。屬性spark.r.shell.command在設置時優先。

SPARK_LOCAL_IP

要綁定到的機器IP地址。

SPARK_PUBLIC_DNS

Spark應用程序將要通知的其他機器的主機名。

除了列出的變量之外,還有設置Spark獨立集羣腳本的選項,比如在每臺機器上使用的內核數量和最大內存。因爲spark-env.sh是一個shell腳本,所以可以通過編程設置其中一些;例如,您可以通過查找特定網絡接口的IP來計算SPARK_LOCAL_IP。

  •  
  •  
注意在cluster模式下對Yarn運行Spark時,需要使用Spark .YARN.appmasterenv設置環境變量。在conf/spark-default .conf文件中設置[EnvironmentVariableName]屬性。在spark-env.sh中設置的環境變量不會在cluster模式下反映在Yarn Application Master中。有關更多信息,請參見Yarn相關的Spark屬性。

 

16.5.8.   應用程序中的Job調度

在給定的Spark應用程序中,如果從單獨的線程提交多個並行作業,則可以同時運行它們。在本節中,我們所說的job指的是一個Spark action和任何需要運行以計算該action的任務。Spark的調度程序是完全線程安全的,並且支持此用例來啓用服務於多個請求的應用程序(例如,針對多個用戶的查詢)。

默認情況下,Spark的調度程序以FIFO方式運行作業。如果隊列頭部的作業不需要使用整個集羣,則稍後的作業可以立即開始運行,但是如果隊列頭部的作業很大,則稍後的作業可能會顯著延遲。

還可以配置作業之間的公平共享。在公平共享下,Spark以循環方式在作業之間分配任務,以便所有作業獲得大致相等的集羣資源共享。這意味着在長作業運行時提交的短作業可以立即開始接收資源,並且仍然可以在不等待長作業完成的情況下獲得良好的響應時間。這種模式最適合多用戶設置。

要啓用公平調度程序,設置spark.scheduler.mode爲Fair,可以在SparkContext中設置。

Fair調度程序還支持將作業分組到池中,併爲每個池設置不同的調度選項或權重。這可以爲更重要的作業創建高優先級池,或者將每個用戶的作業分組在一起,並給用戶平等的共享,而不管他們有多少併發作業,而不是給作業平等的共享。該方法模仿Hadoop Fair調度程序。

在不進行任何干預的情況下,新提交的作業將進入默認池,可以通過設置spark.scheduler.pool屬性來設置作業池。這是這樣做的(假設sc是您的SparkContext:

sc.setLocalProperty("spark.scheduler.pool","pool1")

設置此LocalProperty後,此線程中提交的所有作業都將使用此池名稱。設置爲每個線程,以便讓一個線程可以方便地代表同一個用戶運行多個作業。如果希望清除線程關聯的池,請將其設置爲null。

16.6.  結束語

本章涵蓋了很多關於Spark應用程序的內容;我們學習瞭如何用Spark的所有語言編寫、測試、運行和配置它們。在第17章中,我們將討論在運行Spark應用程序時的部署和集羣管理選項。

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