分布式任务分发引发的问题

问题描述:

昨天有人使用pyspark遇到一个问题,代码简化为:

1
2
3
4
5
6
7
8
9
import one
from pyspark import SparkConf,SparkContext

conf = SparkConf().setMaster("spark://localhost:7077").setAppName('test')
sc = SparkContext(conf=conf)
df = sc.textFile("/Users/scut_DELL/data")

print one.count("test")
df.map(lambda line: one.count("test"))

其中one模块是自己写的在当前目录下的python代码,代码第8行并不会报任何错误,但运行到第9行在map里却抛出找不到one模块。

该问题在分布式系统很常见。spark在执行rdd运行时,需要将rdd的代码和上下文打包发往不同的计算容器,以达到分布式计算的目的。上文代码执行到第9行时,代码执行环境已经改变,不再是当前目录,当然会报找不到one模块。

解决这个问题的办法很简单,就是把one模块所在的代码文件也发往map计算的容器

1
sc = SparkContext(conf=conf, pyFiles=["one.py"])

其他类似的问题:

  • 用mapreduce或者spark计算,如果需要读取本地文件,需要将文件同时发往集群中所有的机器,如果引用某个python包,需要在集群中所有机器都装上该依赖
  • storm在写spout和bolt时,需要将不可序列化的字段加上transient关键字,否则会包序列化失败。原因是storm在分发任务需要将当前的上下文序列化包装成子任务发往worker。通常的做法是把所有的字段加上transient关键字,但这样写会导致构造函数的赋值都无效,而子任务发往worker后反序列化不会调用构造函数。因此storm的spout和bolt不建议写构造函数,而去覆盖prepare方法,需要传递给spout和bolt的参数通过storm的conf来传递。