Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在PySpark中处理数据之前,如何在所有Spark Worker上运行函数?_Python_Apache Spark_Pyspark - Fatal编程技术网

Python 在PySpark中处理数据之前,如何在所有Spark Worker上运行函数?

Python 在PySpark中处理数据之前,如何在所有Spark Worker上运行函数?,python,apache-spark,pyspark,Python,Apache Spark,Pyspark,我正在使用纱线在集群中运行火花流任务。群集中的每个节点都运行多个spark Worker。在流媒体开始之前,我想对集群中所有节点上的所有工作线程执行“设置”功能 流式处理任务将传入消息分类为垃圾邮件或非垃圾邮件,但在此之前,它需要将最新的预训练模型从HDFS下载到本地磁盘,如下面的伪代码示例: def fetch_models(): if hadoop.version > local.version: hadoop.download() 我在这里看到了以下示例:

我正在使用纱线在集群中运行火花流任务。群集中的每个节点都运行多个spark Worker。在流媒体开始之前,我想对集群中所有节点上的所有工作线程执行“设置”功能

流式处理任务将传入消息分类为垃圾邮件或非垃圾邮件,但在此之前,它需要将最新的预训练模型从HDFS下载到本地磁盘,如下面的伪代码示例:

def fetch_models():
    if hadoop.version > local.version:
        hadoop.download()
我在这里看到了以下示例:

sc.parallelize().map(fetch_models)
但是在Spark 1.6中,
parallelize()
需要使用一些数据,比如我现在正在做的这项糟糕的工作:

sc.parallelize(range(1, 1000)).map(fetch_models)
为了确保函数在所有工人身上运行,我将范围设置为1000。我也不知道运行时集群中有多少工人

我已经阅读了编程文档,并在谷歌上搜索了很久,但我似乎找不到任何方法在没有任何数据的情况下向所有员工分发任何东西

完成此初始化阶段后,流式处理任务与往常一样,对来自Kafka的传入数据进行操作

我使用模型的方式是运行类似于以下内容的函数:

spark_partitions = config.get(ConfigKeys.SPARK_PARTITIONS)
stream.union(*create_kafka_streams())\
    .repartition(spark_partitions)\
    .foreachRDD(lambda rdd: rdd.foreachPartition(lambda partition: spam.on_partition(config, partition)))
理论上,我可以在
on_partition
函数中检查模型是否是最新的,尽管在每个批次中这样做确实是浪费。我想在Spark开始从Kafka检索批之前完成,因为从HDFS下载可能需要几分钟

更新:

需要明确的是:这不是关于如何分发文件或如何加载文件的问题,而是关于如何在不操作任何数据的情况下对所有工作人员运行任意方法的问题

要澄清当前实际加载模型的含义:

def on_partition(config, partition):
    if not MyClassifier.is_loaded():
        MyClassifier.load_models(config)

    handle_partition(config, partition)
而MyClassifier是这样的:

class MyClassifier:
    clf = None

    @staticmethod
    def is_loaded():
        return MyClassifier.clf is not None

    @staticmethod
    def load_models(config):
        MyClassifier.clf = load_from_file(config)

静态方法,因为PySpark似乎无法使用非静态方法序列化类(类的状态与其他工作程序的关系无关)。在这里,我们只需调用load_models()一次,并且在以后的所有批处理中都将设置MyClassifier.clf。这是不应该每批都做的事情,这是一次性的事情。与使用fetch_models()从HDFS下载文件相同。

这是Spark的典型用例。假设
fetch\u models
返回模型而不是本地保存,您可以执行以下操作:

bc_models = sc.broadcast(fetch_models())

spark_partitions = config.get(ConfigKeys.SPARK_PARTITIONS)
stream.union(*create_kafka_streams())\
    .repartition(spark_partitions)\
    .foreachRDD(lambda rdd: rdd.foreachPartition(lambda partition: spam.on_partition(config, partition, bc_models.value)))
这确实假设您的模型适合内存中的驱动程序和执行器


您可能担心将模型从单个驱动程序广播到所有执行器效率低下,但它使用了“高效广播算法”,根据

的规定,其性能明显优于通过HDFS进行的分发。如果您只想在工作机之间分发文件,最简单的方法是使用以下机制:

some_path = ...  # local file, a file in DFS, an HTTP, HTTPS or FTP URI.
sc.addFile(some_path)
并使用
SparkFile在workers上检索它。获取
和标准IO工具:

from pyspark import SparkFiles

with open(SparkFiles.get(some_path)) as fw:
    ... # Do something
如果您想确保实际加载了模型,最简单的方法是在模块导入时加载。假设
config
可用于检索模型路径:

  • model.py

    from pyspark import SparkFiles
    
    config = ...
    class MyClassifier:
        clf = None
    
        @staticmethod
        def is_loaded():
            return MyClassifier.clf is not None
    
        @staticmethod
        def load_models(config):
            path = SparkFiles.get(config.get("model_file"))
            MyClassifier.clf = load_from_file(path)
    
    # Executed once per interpreter 
    MyClassifier.load_models(config)  
    
    from pyspark import SparkContext
    
    config = ...
    
    sc = SparkContext("local", "foo")
    
    # Executed before StreamingContext starts
    sc.addFile(config.get("model_file"))
    sc.addPyFile("model.py")
    
    import model
    
    ssc = ...
    stream = ...
    stream.map(model.MyClassifier.do_something).pprint()
    
    ssc.start()
    ssc.awaitTermination()
    
  • main.py

    from pyspark import SparkFiles
    
    config = ...
    class MyClassifier:
        clf = None
    
        @staticmethod
        def is_loaded():
            return MyClassifier.clf is not None
    
        @staticmethod
        def load_models(config):
            path = SparkFiles.get(config.get("model_file"))
            MyClassifier.clf = load_from_file(path)
    
    # Executed once per interpreter 
    MyClassifier.load_models(config)  
    
    from pyspark import SparkContext
    
    config = ...
    
    sc = SparkContext("local", "foo")
    
    # Executed before StreamingContext starts
    sc.addFile(config.get("model_file"))
    sc.addPyFile("model.py")
    
    import model
    
    ssc = ...
    stream = ...
    stream.map(model.MyClassifier.do_something).pprint()
    
    ssc.start()
    ssc.awaitTermination()
    

感谢您的投入。不过,有一个问题是,既然这是一项流媒体任务,而不是批处理作业,spark不会在每个批上分发这些广播模型吗?如果批处理间隔很低(比如5秒),这将是一个瓶颈。或者广播模型是使用SparkContext而不是StreamingSparkContext创建的?因此,模型在流媒体开始之前广播一次,然后在每个批次上重复使用?确切地说,
broadcast
SparkContext
上的一种方法,并且在流媒体开始之前广播一次。请注意文档中关于反序列化每个任务的数据的内容:每个批创建(a)个新任务,这意味着反序列化将针对每个批进行。这有多糟糕取决于您的情况,但至少肯定比每批从磁盘读取文件要好得多。@sgvd如果我错了,请纠正我,但上次我检查Python广播时,它已传递给使用磁盘的工作人员。我是不是遗漏了什么?@sgvd你说的“比每批从磁盘读取文件都好”是什么意思?从本地磁盘读取模型?在批处理开始之前,只执行一次。加载的模型存储为类变量,因此在以后要处理的每个批处理中,不必进行加载,因为它们已经加载。目前,我必须检查每个批次“如果尚未加载,则阻止流并从本地磁盘加载,否则继续”;相反,我希望能够假设它已经为每个批次加载,因为它应该在流媒体开始之前完成。我不知道SparkFile,我不需要直接处理HDFS,谢谢。尽管主要问题仍然存在:如何“在工人身上找回它”。也就是说,在开始流媒体之前,我如何对所有工作人员执行此操作?我不想在每批产品上都这样做,只做一次,因为模型可能很大。在部署阶段,作为一个单独的步骤,而不是尝试使用spark,这样做可能更容易。文件已经存在
SparkFiles.get(某些路径)
仅返回文件所在的本地路径。这里有一些微妙之处。据我所知,流式处理不会在批处理之间重用Python工作者。因此,必须在每个批次上读取。另一方面,使用文件提供了一些有趣的选项。如果你能将你的文件映射到内存,那么你就可以大大减少内存需求。导入解决了这个问题,这让我很沮丧。导入语句是在执行器上运行安装工作的唯一方法,这让人感觉特别不透明。这有什么原因吗?我们可以使用模块导入直接使用的底层机制吗?@retrocookie如果让您感觉更好,您可以使用Borg模式,但在d结尾