为了执行做业,Spark将RDD操做的处理分解为tasks,每一个task由Executor执行。在执行以前,Spark会计算task的闭包。闭包是Executor在RDD上进行计算的时候必须可见的那些变量和方法(在这种状况下是foreach())。闭包会被序列化并发送给每一个Executor。apache
若是在涉及到的全部的变量中有任何不支持序列化或没有指明如何序列化本身时,你就会遇到这样的错误:编程
org.apache.spark.SparkException: Task not serializable
在编写Spark程序中,因为在map等算子内部使用了外部定义的变量和函数,从而引起Task未序列化问题。然而,Spark算子在计算过程当中使用外部变量在许多情形下确实在所不免,好比在filter算子根据外部指定的条件进行过滤,map根据相应的配置进行变换等。为了解决上述Task未序列化问题,这里对其进行了研究和总结。网络
出现“org.apache.spark.SparkException: Task not serializable”这个错误,通常是由于在map、filter等的参数使用了外部的变量,可是这个变量不能序列化(不是说不能够引用外部变量,只是要作好序列化工做,具体后面详述)。其中最广泛的情形是:当引用了某个类(常常是当前类)的成员函数或变量时,会致使这个类的全部成员(整个类)都须要支持序列化。虽然许多情形下,当前类使用了“extends Serializable”声明支持序列化,可是因为某些字段不支持序列化,仍然会致使整个类序列化时出现问题,最终致使出现Task未序列化问题。闭包
引用成员变量的实例分析
如上所述,因为Spark程序中的map、filter等算子内部引用了类成员函数或变量致使须要该类全部成员都须要支持序列化,又因为该类某些成员变量不支持序列化,最终引起Task没法序列化问题。解决方法,将不须要序列化的的成员变量使用关键字“@transent”标注。并发
引用成员函数的实例分析
成员变量与成员函数的对序列化的影响相同,即引用了某类的成员函数,会致使该类全部成员都支持序列化。函数
如上所述,引用了某类的成员函数,会致使该类及全部成员都须要支持序列化。所以,对于使用了某类成员变量或函数的情形,首先该类须要序列化(extends Serializable),同时须要对某些不须要序列化的成员变量标记以免为序列化形成影响。
map等算子内部能够引用外部变量和某类的成员变量,可是要作好该类的序列化处理。首先是该类须要继承Serializable类,此外,对于类中某些序列化会出错的成员变量作好处理,这也是Task未序列化问题的主要缘由。对于出现这类问题,首先查看未能序列化的成员变量是哪一个,对于能够不须要序列化的成员变量可以使用“@transent”标注。
此外,也不是map操做所在的类必须序列化不可(继承Serializable类),对于不须要引用某类成员变量或函数的情形,就不会要求相应的类必须实现序列化。spa
解决办法与编程建议
承上所述,这个问题主要是引用了某类的成员变量或函数,而且相应的类没有作好序列化处理致使的。所以解决这个问题无非如下两种方法:
不在(或不直接在)map等闭包内部直接引用某类(一般是当前类)的成员函数或成员变量
若是引用了某类的成员函数或变量,则需对相应的类作好序列化处理
(一)不在(或不直接在)map等闭包内部直接引用某类成员函数或成员变量
若是程序依赖的值相对固定,可取固定的值,或定义在map、filter等操做内部,或定义在scala
object对象中(相似于Java中的static变量),把它声明为一个全局静态的变量就能够绕过序列化
若是依赖值须要程序调用时动态指定(以函数参数形式),则在map、filter等操做时,可不直接引用该成员变量,而是在函数中根据成员变量的值从新定义一个局部变量,这样map等算子就无需引用类的成员变量。scala
(二)若是引用了某类的成员函数或变量,则需对相应的类作好序列化处理
对于这种状况,则需对该类作好序列化处理,首先该类继承序列化类,而后对于不能序列化的成员变量使用“@transent”标注,告诉编译器不须要序列化。
此外若是能够,可将依赖的变量独立放到一个小的class中,让这个class支持序列化,这样作能够减小网络传输量,提升效率。code