在Spark中:
driver是運行用戶編寫Application的main()函數的地方,具體負責DAG的構建、任務的划分、task的生成與調度等。job,stage,task生成都離不開rdd自身,rdd的相關的操作不能缺少driver端的sparksession/sparkcontext。
executor是真正執行task地方,而task執行離不開具體的數據,這些task運行的結果可以是shuffle中間結果,也可以持久化到外部存儲系統。一般都是將結果、狀態等匯集到driver。但是,目前executor之間不能互相通信,只能借助第三方來實現數據的共享或者通信。
那么,編寫的Spark程序代碼,運行在driver端還是executor端呢?
先看個簡單例子:通常我們在本地測試程序的時候,要打印RDD中的數據。
在本地模式下,直接使用rdd.foreach(println)或rdd.map(println)在單台機器上,能夠按照預期打印並輸出所有RDD的元素。
但是,在集群模式下,由executor執行輸出寫入的是executor的stdout,而不是driver上的stdout,所以driver的stdout不會顯示這些!
要想在driver端打印所有元素,可以使用collect()方法先將RDD數據帶到driver節點,然后在調用foreach(println)(但需要注意一點,由於會把RDD中所有元素都加載到driver端,可能引起driver端內存不足導致OOM。如果你只是想獲取RDD中的部分元素,可以考慮使用take或者top方法)
總之,在這里RDD中的元素即為具體的數據,對這些數據的操作都是由負責task執行的executor處理的,所以想在driver端輸出這些數據就必須先將數據加載到driver端進行處理。
最后做個總結:所有對RDD具體數據的操作都是在executor上執行的,所有對rdd自身的操作都是在driver上執行的。比如foreach、foreachPartition都是針對rdd內部數據進行處理的,所以我們傳遞給這些算子的函數都是執行於executor端的。但是像foreachRDD、transform則是對RDD本身進行一列操作,所以它的參數函數是執行在driver端的,那么它內部是可以使用外部變量,比如在Spark Streaming程序中操作offset、動態更新廣播變量等。