I have a global boolean variable that controls a loop in the main function to determine whether or not to start over. The decision is made in a reduce where the variable is set to true if a condition is met. Then in the main, if the condition is met it runs the entire program again. This works on one machine but when I try to run it on Amazons EC2 it does not loop through.
3 回答
问题在于,在 EC2 上,您以真正的分布式模式运行(多台机器,每台机器运行自己的 JVM),而在您的机器上,您以独立模式(仅使用 1 个 JVM)运行。
当您在您的机器上运行作业时,hadoop 框架会在同一个 JVM 中运行客户端(所谓的驱动程序)、map 任务和 reduce 任务。因此,您的 reduce 函数和 main 将能够使用 SAME 全局变量。(附带说明,我认为这个全局变量应该是静态的,因为 hadoop 框架可能会为客户端、映射、reduce 执行创建多个对象。如果您没有将其设为静态并且它仍然可以在您的机器上运行,则意味着它使用同一个对象)。
当您在 EC2 上运行作业时,您将在不同的 JVM 中运行客户端、map 任务和 reduce 任务。因此,reducer 将修改驻留在当前 JVM 中的对象的“全局”变量,因此运行客户端的 JVM(在驻留在其 JVM 中的对象中拥有自己的全局变量版本)将看不到所做的修改由减速器到“全局”变量。
请注意 global 周围的引号。它们表示变量并不像您期望的那样全局。
您可以将变量从减速器传递回驱动程序(主类),方法是将该变量的值写入文件并将该文件放在分布式缓存中,或者将变量的值写入作业的输出中( part-r-xxxx 文件),以防您可以将其与实际输出/有效负载分开。
在这两种情况下,驱动程序都可以从缓存中检索文件,也可以从作业的输出目录中检索输出文件。读入变量的值,并在此基础上做出决定。
如果您想计算有多少减速器达到特定状态。您可以使用用户定义的计数器(当达到该状态时,每个 reducer 将递增该计数器),您可以在映射器中对其进行解释。可以使用 Mapper.Context/Reducer.Context 的 getCounter() 方法通过其名称访问计数器
我最终用计数器做了这个,它不那么漂亮,但它有效。我创建了两个变量 cnt 和 cnt1。如果满足条件,我使用计数器递增。cnt 保存上次循环作业时的计数 cnt1 在作业后更新,如果它们相同,则不会再次循环。这最终会额外运行所有作业一次,但它会提供正确的输出。
谢谢您的帮助。
正如 Razvan 所提到的,您的 map reduce 作业中不能有全局变量,因为您的代码在多台机器(分布式)上并行运行。服务器 A 上的 reduce 作业(并且必须用于 MapReduce)完全独立于机器 B 上运行的代码。在重新启动之前,您必须等待作业完成(成功与否)。如果您可以等待完成,您可以在配置中设置一个变量或使用服务器计数器作为您的“全局变量”,并在作业停止时引用它。如果您使用的是 HBase,您可以在 HBase 中设置一个值并根据需要读取它的作业完成情况。
除非您已经设置好 HBase,否则您的情况可能会让 HBase 有点矫枉过正。
要设置配置,请在主驱动程序函数中执行以下操作:
conf.set("variableName",variableValue);
要访问 Mapper 或 Reducer 中的配置:
Configuration conf = context.getConfiguration();
String variableValue = conf.get("variableName");
请注意,每个 mapper 或 reducer 都不会看到另一个 mapper 或 reducer 对变量所做的任何更改。我不确定您是否甚至想在映射器或减速器中修改 conf 变量——这可能不是最佳实践。
对于计数器,您可以在映射器或减速器中执行以下操作(将计数器增加 1):
context.getCounter("counterName").increment(new Long(1));
当您的 MR 工作完成后,您可以通过以下方式引用计数器:
Counters counters = job.getCounters();
Counter counter = counters.findCounter("counterName");
long cntVal = counter.getValue();
同样,您只能在 MR 作业完成后信任您的计数器值,而不是在 MR 作业运行时。