【问题标题】:MapReduce how to allow Mapper to read an xml file for lookupMapReduce 如何允许 Mapper 读取 xml 文件进行查找
【发布时间】:2015-12-23 01:13:41
【问题描述】:

在我的 MapReduce 作业中,我将产品名称作为字符串参数传递给 Mapper。 Mapper.py 脚本导入一个名为 Process.py 的辅助脚本,该脚本对产品名称进行处理并将一些发射字符串返回给 Mapper。然后映射器将这些字符串发送到 Hadoop 框架,以便它们可以被 Reducer 拾取。一切正常,除了以下几点:

Process.py 脚本包含一个查找值字典,我希望将其从脚本内部移动到 xml 文件中,以便于更新。我已经在本地对此进行了测试,如果我在 Process.py 脚本中包含 xml 文件的 Windows 路径,它可以正常工作。但是,由于某种原因,在 Hadoop MapReduce 环境中测试它不起作用。

我尝试在 Process.py 脚本中指定 xml 文档的 HDFS 路径,并尝试在 MapReduce 作业命令中将 xml 文档的名称作为 -file 参数添加,但均未奏效。

例如,在 Process.py 里面,我试过:
xml_file = r'appers@hdfs.network.com:/nfs_home/appers/cnielsen/product_lookups.xml'

xml_file = r'/nfs_home/appers/cnielsen/product_lookups.xml'

在 MapReduce 命令中,我将 xml 文件的名称作为 -file 参数包含在内。例如:
... -file product_lookups.xml -reducer ...

问题是:在MapReduce环境下,如何让Process.py脚本读取这个存储在HDFS上的xml文档?

【问题讨论】:

    标签: python hadoop mapreduce


    【解决方案1】:

    这是一个端到端的示例,它调整了上一个问题中提到的技术,以更贴近您的问题。

    Python read file as stream from HDFS

    这是一个小型 Python Hadoop 流应用程序,它读取键值对,根据存储在 HDFS 中的 XML 配置文件检查键,然后仅当键与配置匹配时才发出值。匹配逻辑被卸载到一个单独的 Process.py 模块中,该模块通过使用对 hdfs dfs -cat 的外部调用从 HDFS 读取 XML 配置文件。

    首先,我们创建一个名为 pythonapp 的目录,其中包含用于我们实现的 Python 源文件。稍后我们会在提交流式作业时看到,我们将在 -files 参数中传递此目录。

    为什么我们将文件放入中间目录而不是在-files 参数中单独列出每个文件?这是因为当 YARN 本地化文件以在容器中执行时,它引入了一个符号链接间接层。然后 Python 无法通过符号链接正确加载模块。解决方案是将两个文件打包到同一个目录中。然后,当 YARN 本地化文件时,符号链接间接在目录级别完成,而不是在单个文件。由于主脚本和模块在物理上位于同一目录中,因此 Python 将能够正确加载模块。这个问题更详细地解释了这个问题:

    How to import a custom module in a MapReduce job?

    映射器.py

    import subprocess
    import sys
    from Process import match
    
    for line in sys.stdin:
        key, value = line.split()
        if match(key):
            print value
    

    进程.py

    import subprocess
    import xml.etree.ElementTree as ElementTree
    
    hdfsCatProcess = subprocess.Popen(
            ['hdfs', 'dfs', '-cat', '/pythonAppConf.xml'],
            stdout=subprocess.PIPE)
    pythonAppConfXmlTree = ElementTree.parse(hdfsCatProcess.stdout)
    matchString = pythonAppConfXmlTree.find('./matchString').text.strip()
    
    def match(key):
        return key == matchString
    

    接下来,我们将 2 个文件放入 HDFS。 /testData 是输入文件,包含制表符分隔的键值对。 /pythonAppConf.xml 是 XML 文件,我们可以在其中配置一个特定的键来匹配。

    /testData

    foo 1
    bar 2
    baz 3
    

    /pythonAppConf.xml

    <pythonAppConf>
        <matchString>foo</matchString>
    </pythonAppConf>
    

    由于我们已将matchString 设置为foo,并且由于我们的输入文件仅包含一条键设置为foo 的记录,因此我们希望运行作业的输出是包含相应值的单行键入foo,即1。进行测试运行,我们确实得到了预期的结果。

    > hadoop jar share/hadoop/tools/lib/hadoop-streaming-*.jar \
          -D mapreduce.job.reduces=0 \
          -files pythonapp \
          -input /testData \
          -output /streamingOut \
          -mapper 'python pythonapp/Mapper.py'
    
    > hdfs dfs -cat /streamingOut/part*
    1   
    

    另一种方法是在 -files 参数中指定 HDFS 文件。这样,YARN 将在 Python 脚本启动之前将 XML 文件作为本地化资源拉取到运行容器的各个节点。然后,Python 代码可以打开 XML 文件,就好像它是工作目录中的本地文件一样。对于运行多个任务/容器的大型作业,此技术可能优于从每个任务调用 hdfs dfs -cat

    为了测试这种技术,我们可以尝试不同版本的 Process.py 模块。

    进程.py

    import xml.etree.ElementTree as ElementTree
    
    pythonAppConfXmlTree = ElementTree.parse('pythonAppConf.xml')
    matchString = pythonAppConfXmlTree.find('./matchString').text.strip()
    
    def match(key):
        return key == matchString
    

    命令行调用更改为在-files 中指定一个HDFS 路径,我们再次看到了预期的结果。

    > hadoop jar share/hadoop/tools/lib/hadoop-streaming-*.jar \
          -D mapreduce.job.reduces=0 \
          -files pythonapp,hdfs:///pythonAppConf.xml \
          -input /testData \
          -output /streamingOut \
          -mapper 'python pythonapp/Mapper.py'
    
    > hdfs dfs -cat /streamingOut/part*
    1   
    

    Apache Hadoop 文档在这里讨论了使用 -files 选项在本地提取 HDFS 文件。

    http://hadoop.apache.org/docs/r2.7.1/hadoop-streaming/HadoopStreaming.html#Working_with_Large_Files_and_Archives

    【讨论】:

    • 我的问题与您提供的上一个问题中的问题不同。我在大型数据集上使用流式 MapReduce,但不是让 Mapper 完成所有繁重的工作,而是使用一个名为 Process.py 的辅助脚本。 Mapper &lt;--&gt; Process.py Mapper 仍然将输出流式传输到 Reducer。我希望 Process.py 脚本能够读取驻留在 HDFS 上的小型 xml 文件,以便在内存中构建 Python 字典。这在我的 PC 上本地工作,因为我可以只提供 xml 文件的路径并使用 lxml 来解析它。
    • @ChrisNielsen,同样的技术可以应用于您的问题。我编辑了答案以显示完整的端到端工作示例。我还描述了如何使用 -files 参数告诉 YARN 在 Python 脚本运行之前将文件作为本地资源拉取,这可能更简单,并且可能为非常大的作业提供更好的性能。
    • 太棒了!不仅有一个详细的选项,而且有两个!感谢您花时间回答这个问题!我已经尝试了第二个选项,并且效果很好!
    【解决方案2】:

    感谢 Chris Nauroth 在上面提供的答案。通过这篇文章,我想准确总结一下解决我的问题的原因。

    他提供的第二个答案与我最初想要做的非常接近。我发现,我只需要几个小改动就可以让它工作。例如,在 Process.py 脚本中,我之前尝试包含一个完整路径到小型查找 xml,如下所示:

    xml_file = r'appers@hdfs.network.com:/nfs_home/appers/cnielsen/product_lookups.xml'

    xml_file = r'/nfs_home/appers/cnielsen/product_lookups.xml'

    原来我需要做的只是在我的 Process.py 脚本中提供文件的名称,而无需路径。例如:
    xml_file = 'product_lookups.xml'

    然后对于实际的 Hadoop 命令,我之前尝试过这个没有成功:(在 -mapper 列表之后使用 -file product_lookups.xml)

      > hadoop jar /share/hadoop/tools/lib/hadoop-streaming.jar \
      -file /nfs_home/appers/cnielsen/Mapper.py \
      -file /nfs_home/appers/cnielsen/Reducer.py \
      -mapper '/usr/lib/python_2.7.3/bin/python Mapper.py ProductName' \
      -file Process.py \
      -file product_lookups.xml \
      -reducer '/usr/lib/python_2.7.3/bin/python Reducer.py' \
      -input /nfs_home/appers/extracts/*/*.xml \
      -output /user/lcmsprod/output/cnielsen/test47
    

    构建 Hadoop 命令的正确方法是使用 -files 并在任何其他文件列表之前列出此查找文件。例如,这有效:

      > hadoop jar /share/hadoop/tools/lib/hadoop-streaming.jar \
      -files /nfs_home/appers/cnielsen/product_lookups.xml \
      -file /nfs_home/appers/cnielsen/Mapper.py \
      -file /nfs_home/appers/cnielsen/Reducer.py \
      -mapper '/usr/lib/python_2.7.3/bin/python Mapper.py ProductName' \
      -file Process.py \
      -reducer '/usr/lib/python_2.7.3/bin/python Reducer.py' \
      -input /nfs_home/appers/extracts/*/*.xml \
      -output /user/lcmsprod/output/cnielsen/test47
    

    注意:尽管this page 说要像这样构造 -files 命令:

    -files hdfs://host:fs_port/user/testfile.txt

    如果我包含 hdfs:// 或 host: 部分,它对我不起作用,从上面列出的实际命令可以看出。

    【讨论】:

    • 感谢您在这里总结。我还想提一下,我将 2 个 Python 文件打包到一个中间目录以传递给 -files 参数是有原因的。我刚刚编辑了我的答案来解释这一点,并链接到以前的答案,该答案提供了对该部分的更详细的解释。
    • 是的,我注意到您在此处有中间文件夹的名称,在列出 xml 文件之前有一个逗号:-files pythonapp,hdfs:///pythonAppConf.xml 我应该这样做吗?
    • 是的,我希望将逗号分隔的列表传递给-files 会起作用。不推荐使用 -file 选项,而赞成使用 -files
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 2014-04-29
    • 1970-01-01
    相关资源
    最近更新 更多