在spark中,RDD的創建和初始化是在Driver中執行的,而實際操作是在Executor中進行。如果要涉及到傳遞一些方法或者屬性的時候,那麼就要序列化之後才能進行傳遞。下面用兩個例子來進行說明:
- 函數的傳遞
先看代碼:
object SerializableTest {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SerializableTest")
val sc = new SparkContext(conf)
val dataRDD: RDD[String] = sc.makeRDD(List("hadoop", "hive", "spark", "scala"))
val test = new Test("h")
val result: RDD[String] = test.test1(dataRDD)
result.collect().foreach(println)
}
}
//RDD中的函數傳遞和屬性
class Test(s: String){
//定義函數,在driver中執行
def isContain(data: String): Boolean = {
data.contains(s)
}
//傳遞函數,在Executor中執行
def test1(rdd: RDD[String]): RDD[String] = {
rdd.filter(isContain)
}
}
正如上面所說,初始化的工作是在Driver中進行的,所以Test類在初始化的時候就是在Driver中進行的,後面調用test1方法的時候是在Executor中進行的,而test1中調用了isContain方法,這個方法是在Driver進行的,因爲我這裏是在單機上部署的spark,所以要涉及到進程間的通信了,如果在進羣中運行的話,就涉及到網絡的通信。無論是進程間得到通信還是網絡間的通信,一個普通的類是不能傳輸的,所以要把Test類混入序列化的特質才行,如上面代碼沒有混入特質,執行效果就如下面所示:
所以解決方案是:在Test類中混入Serializable特質,class Test(s: String) extends Serializable{}
混入特質後就能成功運行了。
- 屬性的傳遞
再看代碼:
object SerializableTest {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SerializableTest")
val sc = new SparkContext(conf)
val dataRDD: RDD[String] = sc.makeRDD(List("hadoop", "hive", "spark", "scala"))
val test = new Test("h")
val result: RDD[String] = test.test2(dataRDD)
result.collect().foreach(println)
}
}
//RDD中的函數傳遞和屬性
class Test(s: String) extends Serializable{
//定義函數,在driver中執行
def isContain(data: String): Boolean = {
data.contains(s)
}
//傳遞屬性,在Executor中執行
def test2(rdd: RDD[String]): RDD[String] = {
rdd.filter(x => x.contains(s))
}
}
這個和上面的代碼類似,只不過這次傳遞的只是一個屬性s,但是因爲也涉及到進程的通信或者網絡傳輸,所以都要混入序列化特質才能成功運行。