Spark2.x 學習筆記(一) - Spark Core

1、Spark 介紹

Apache Spark 是專爲大規模數據處理而設計的快速通用的計算引擎。Spark是Scala編寫,方便快速編程; Spark 技術棧中包括 SparkCore,SparkStreaming,SparkSQL,SparkMllib等。
Spark與MapReduce的區別
1. Spark 基於內存迭代處理數據,MR基於磁盤迭代處理數據
2. Spark 粗粒度資源申請,MR是細粒度資源申請
3. MR中只有mapper,reducer,相當於spark中的map和reduceByKey兩個算子,在MR業務邏輯要自己實現,Spark中有各種算子對應各種業務
Spark 運行模式分爲 :

  • 本地模式 用於本地開發,多用於測試
  • standalone Spark自帶的資源調度框架,支持分佈式搭建,Spark 可以基於Standalone運行任務
  • yarn Hadoop生態圈中的資源調度框架,Spark也支持在Yarn中運行

2、spark 本地方式運行

導入pom

<dependency>
	<groupId>org.apache.spark</groupId>
	<artifactId>spark-core_2.11</artifactId>
	<version>2.3.2</version>
</dependency>
public class WordCount2 {
    public static void main(String[] args) {
        SparkConf sparkConf = new SparkConf().setMaster("local").setAppName("wc_2");
        JavaSparkContext sc = new JavaSparkContext(sparkConf);
        JavaRDD<String> text = sc.textFile("test.txt");
        // 將文件中字符串拆分
        JavaRDD<String> word = text.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterator<String> call(String s) throws Exception {
                String[] str = s.split(" ");
                return Arrays.asList(str).iterator();
            }
        });
        // 轉換爲map 格式    例如"aa" -> {"aa",1}
     JavaPairRDD<String, Integer> paris = word.mapToPair(new PairFunction<String, String, Integer>() {
         @Override
         public Tuple2<String, Integer> call(String s) throws Exception {
             Tuple2<String,Integer> tuple2 = new Tuple2<>(s,1);
             return tuple2;
         }
     });
     // 根據key 相同的疊加,  先分組再計算
     JavaPairRDD<String,Integer> count =  paris.reduceByKey(new Function2<Integer, Integer, Integer>() {
         @Override
         public Integer call(Integer integer, Integer integer2) throws Exception {
             return integer+integer2;
         }
     });
     // map 中 key 和 value 調換, 調換後key爲Integer格式,以便後面根據 key 排序,
     JavaPairRDD<Integer,String> tem =  count.mapToPair(new PairFunction<Tuple2<String,Integer>, Integer, String>() {
         @Override
         public Tuple2<Integer, String> call(Tuple2<String, Integer> tuple2) throws Exception {
             return new Tuple2<>(tuple2._2,tuple2._1);
         }
     });
        // 排序
        JavaPairRDD<Integer,String> sorted =  tem.sortByKey(false);

        //遍歷打印
        sorted.foreach(new VoidFunction<Tuple2<Integer, String>>() {
         @Override
         public void call(Tuple2<Integer, String> integerStringTuple2) throws Exception {
             System.out.println(integerStringTuple2._1+":"+integerStringTuple2._2);
         }
     });
    }
}

spark 是scala 實現的, 用java 感覺麻煩的可以換用scala 去實現; 以下是scala 實現上述功能

object SparkTest {
  def main(args: Array[String]): Unit = {
    var conf = new SparkConf().setMaster("local").setAppName("wc")
    var sc = new SparkContext(conf)
    var text = sc.textFile("test.txt")
    var word = text.flatMap((_.split(" ")))
    var pair = word.map((_,1))
    var result = pair.reduceByKey(_+_)
    var sort:RDD[(Int, String)] = result.map(tuple=>(tuple._2,tuple._1));
    var res =sort.sortByKey()
    res.foreach(println)
  }
}

在上述看到了一堆RDD格式,下面介紹什麼是RDD
RDD(Resilient Distributed Dateset),彈性分佈式數據集。
RDD 的五大特徵:

  1. RDD由一系列partition組成
  2. 算子(函數)是作用在partition上的
  3. RDD 之間有依賴關係
  4. 分區器是作用在K,V格式的RDD上
  5. partition對外提供最佳的計算位置,利於數據處理的本地化

什麼是K,V格式的RDD?
RDD裏面存儲的數據都是二元組對象,那麼這個RDD我們就叫做K,V格式的RDD,類似Map<Object,Object>。
哪裏體現RDD的彈性(容錯)?
partition數量,大小沒有限制,體現了RDD的彈性。
RDD之間依賴關係,可以基於上一個RDD重新計算出RDD。
哪裏體現RDD的分佈式?
RDD是由Partition組成,partition是分佈在不同節點上的

standalone 環境搭建

node1 爲master; node2 node3 node4 爲worker

1、下載安裝包 http://spark.apache.org/downloads.html
並解壓

2、進入安裝包conf 文件夾下,配置slave

[root@node2 conf]# mv slaves.template slaves
[root@node2 conf]# vim slaves
node2
node3
node4

3、 配置spark-env.sh

export SPARK_MASTER_HOST=node1
export SPARK_MASTER_PORT=7077
export SPARK_WORKER_CORES=1 
export SPARK_WORKER_MEMORY=1g

export SPARK_MASTER_HOST=node1 設置master 主機
export SPARK_MASTER_PORT=7077 提交任務的端口,默認是7077
export SPARK_WORKER_CORES=1 每個worker從節點能夠支配的core的個數
export SPARK_WORKER_MEMORY=1g 每個worker從節點能夠支配的內存數

4、 同步 node2 node3 node4 節點, 並啓動集羣
進入sbin 目錄下./start-all.sh

5、 搭建上傳任務的客戶端(可選)
將spark安裝包原封不動的拷貝到一個新的節點上,然後,在新的節點上提交任務即可,也可以在master和worker 上提交

6、 輸入node1:8080 端口,可進入webUI 界面
可以查看啓動的worker,和每個worker的內存

注意:

  • 8080是Spark WEBUI界面的端口,7077是Spark任務提交的端口。
  • 修改master的WEBUI端口, 修改start-master.sh即可,SPARK_MASTER_WEBUI_PORT=8080

standalone兩種任務提交模式:

Standalone-client提交任務方式
以啓動安裝包中的一個Demo PI爲例:

./spark-submit --master spark://node2:7077 --class org.apache.spark.examples.JavaSparkPi /opt/spark-2.3.2-bin-hadoop2.7/examples/jars/spark-examples_2.11-2.3.2.jar 100
./spark-submit 
--master spark://node1:7077 
--deploy-mode client 
--class org.apache.spark.examples.SparkPi 
../lib/spark-examples-1.6.0-hadoop2.6.0.jar 
100

client 模式任務流程:
在這裏插入圖片描述
執行流程:

  1. client模式提交任務後,會在客戶端啓動Driver進程。
  2. Driver會向Master申請啓動Application啓動的資源。
  3. 資源申請成功,Driver端將task發送到worker端執行。
  4. worker將task執行結果返回到Driver端

client模式適用於測試調試程序。Driver進程是在客戶端啓動的,這裏的客戶端就是指提交應用程序的當前節點。在Driver端可以看到task執行的情況。

Standalone-cluster提交任務方式:

  [root@node2 bin]# ./spark-submit --master spark://node2:7077 --deploy-mode cluster --class org.apache.spark.examples.JavaSparkPi /opt/spark-2.3.2-bin-hadoop2.7/examples/jars/spark-examples_2.11-2.3.2.jar 100

任務流程圖:
在這裏插入圖片描述

執行流程:

  1. cluster模式提交應用程序後,會向Master請求啓動Driver.
  2. Master接受請求,隨機在集羣一臺節點啓動Driver進程。
  3. Driver啓動後爲當前的應用程序申請資源。
  4. Driver端發送task到worker節點上執行。
  5. worker將執行情況和執行結果返回給Driver端。

2. Yarn模式兩種提交任務方式

在提交任務的節點添加配置

HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop

yarn-cilent方式

運行demo :

./spark-submit --master yarn --class org.apache.spark.examples.JavaSparkPi /opt/spark-2.3.2-bin-hadoop2.7/examples/jars/spark-examples_2.11-2.3.2.jar 100

在這裏插入圖片描述
執行流程:

  1. 客戶端提交一個Application,在客戶端啓動一個Driver進程。
  2. 應用程序啓動後會向RS(ResourceManager)發送請求,啓動AM(ApplicationMaster)的資源。
  3. RS收到請求,隨機選擇一臺NM(NodeManager)啓動AM。這裏的NM相當於Standalone中的Worker節點。
  4. AM啓動後,會向RS請求一批container資源,用於啓動Executor.
  5. RS會找到一批NM返回給AM,用於啓動Executor。
  6. AM會向NM發送命令啓動Executor。
  7. Executor啓動後,會反向註冊給Driver,Driver發送task到Executor,執行情況和結果返回給Driver端

ApplicationMaster的作用:

  1. 爲當前的Application申請資源
  2. 給NameNode發送消息啓動Executor。
    注意:ApplicationMaster有launchExecutor和申請資源的功能,並沒有作業調度的功能。

yarn-cluster提交任務方式:

./spark-submit --master yarn --cluster org.apache.spark.examples.JavaSparkPi /opt/spark-2.3.2-bin-hadoop2.7/examples/jars/spark-examples_2.11-2.3.2.jar 100

執行流程:
在這裏插入圖片描述

執行流程

  1. 客戶機提交Application應用程序,發送請求到RS(ResourceManager),請求啓動AM(ApplicationMaster)。
  2. RS收到請求後隨機在一臺NM(NodeManager)上啓動AM(相當於Driver端)。
  3. AM啓動,AM發送請求到RS,請求一批container用於啓動Executor。
  4. RS返回一批NM節點給AM。
  5. AM連接到NM,發送請求到NM啓動Executor。
  6. Executor反向註冊到AM所在的節點的Driver。Driver發送task到Executor。

總結
Yarn-Cluster主要用於生產環境中,因爲Driver運行在Yarn集羣中某一臺nodeManager中,每次提交任務的Driver所在的機器都是隨機的,不會產生某一臺機器網卡流量激增的現象,缺點是任務提交後不能看到日誌。只能通過yarn查看日誌。

停止集羣任務命令:yarn application -kill applicationID

Spark 算子

1、Transformation 算子 (懶執行,需要Action算子觸發)

filter: 過濾字符,返回true 的通過
map(mapToPair):轉換爲map 格式 例如"aa" -> {“aa”,1}
flatMap: 文件中字符串拆分
reduceByKey:
sortBy/sortByKey : tem.sortByKey(false);
sample: 抽樣
join:合併

2、Action 算子

foreach:
count:
collect():會將結果回收到Driver端
foreachPartition:
reduce:
countByKey:
countByValue:

任務: Application-> Job -> Stage -> Task
資源: Master -> Worker -> Executer -> ThreadPool

Spark資源調度和任務調度

在這裏插入圖片描述

Spark資源調度和任務調度的流程:
啓動集羣后,Worker節點會向Master節點彙報資源情況,Master掌握了集羣資源情況。當Spark提交一個Application後,根據RDD之間的依賴關係將Application形成一個DAG有向無環圖。任務提交後,Spark會在Driver端創建兩個對象:DAGScheduler和TaskScheduler,DAGScheduler是任務調度的高層調度器,是一個對象。DAGScheduler的主要作用就是將DAG根據RDD之間的寬窄依賴關係劃分爲一個個的Stage,然後將這些Stage以TaskSet的形式提交給TaskScheduler(TaskScheduler是任務調度的低層調度器,這裏TaskSet其實就是一個集合,裏面封裝的就是一個個的task任務,也就是stage中的並行度task任務),TaskSchedule會遍歷TaskSet集合,拿到每個task後會將task發送到計算節點Executor中去執行(其實就是發送到Executor中的線程池ThreadPool去執行)。task在Executor線程池中的運行情況會向TaskScheduler反饋,當task執行失敗時,則由TaskScheduler負責重試,將task重新發送給Executor去執行,默認重試3次。如果重試3次依然失敗,那麼這個task所在的stage就失敗了。stage失敗了則由DAGScheduler來負責重試,重新發送TaskSet到TaskSchdeuler,Stage默認重試4次。如果重試4次以後依然失敗,那麼這個job就失敗了。job失敗了,Application就失敗了。
TaskScheduler不僅能重試失敗的task,還會重試straggling(落後,緩慢)task(也就是執行速度比其他task慢太多的task)。如果有運行緩慢的task那麼TaskScheduler會啓動一個新的task來與這個運行緩慢的task執行相同的處理邏輯。兩個task哪個先執行完,就以哪個task的執行結果爲準。這就是Spark的推測執行機制。在Spark中推測執行默認是關閉的。推測執行可以通過spark.speculation屬性來配置。
注意:
對於ETL類型要入數據庫的業務要關閉推測執行機制,這樣就不會有重複的數據入庫。
如果遇到數據傾斜的情況,開啓推測執行則有可能導致一直會有task重新啓動處理相同的邏輯,任務可能一直處於處理不完的狀態。

圖解Spark資源調度和任務調度的流程
在這裏插入圖片描述

粗粒度資源申請和細粒度資源申請

粗粒度資源申請(Spark)
在Application執行之前,將所有的資源申請完畢,當資源申請成功後,纔會進行任務的調度,當所有的task執行完成後,纔會釋放這部分資源。

優點:在Application執行之前,所有的資源都申請完畢,每一個task直接使用資源就可以了,不需要task在執行前自己去申請資源,task啓動就快了,task執行快了,stage執行就快了,job就快了,application執行就快了。

缺點:直到最後一個task執行完成纔會釋放資源,集羣的資源無法充分利用。

細粒度資源申請(MapReduce)
Application執行之前不需要先去申請資源,而是直接執行,讓job中的每一個task在執行前自己去申請資源,task執行完成就釋放資源。

優點:集羣的資源可以充分利用。
缺點:task自己去申請資源,task啓動變慢,Application的運行就相應的變慢了。

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