Запуск задания Hadoop из другой программы Java

Я пишу программу, которая получает исходный код картографов/редукторов, динамически компилирует мапперы/редукторы и создает из них JAR-файл. Затем он должен запустить этот JAR-файл в кластере Hadoop.

В последней части я динамически настраиваю все необходимые параметры через свой код. Однако проблема, с которой я столкнулся сейчас, заключается в том, что коду требуются скомпилированные классы преобразователя и редуктора во время компиляции. Но на момент компиляции у меня нет этих классов и они позже будут получены во время выполнения (например, через сообщение, полученное от удаленного узла). Буду признателен за любую идею/предложение о том, как решить эту проблему?

Ниже вы можете найти код для моей последней части с проблемой в job.setMapperClass(Mapper_Class.class) и job.setReducerClass(Reducer_Class.class), требующих, чтобы файлы классов (Mapper_Class.class и Reducer_Class.class) присутствовали в время составления:

    private boolean run_Hadoop_Job(String className){
try{
    System.out.println("Starting to run the code on Hadoop...");
    String[] argsTemp = { "project_test/input", "project_test/output" };
    // create a configuration
    Configuration conf = new Configuration();
    conf.set("fs.default.name", "hdfs://localhost:54310");
    conf.set("mapred.job.tracker", "localhost:54311");
    conf.set("mapred.jar", jar_Output_Folder+ java.io.File.separator 
                            + className+".jar");
    conf.set("mapreduce.map.class", "Mapper_Reducer_Classes$Mapper_Class.class");
    conf.set("mapreduce.reduce.class", "Mapper_Reducer_Classes$Reducer_Class.class");
    // create a new job based on the configuration
    Job job = new Job(conf, "Hadoop Example for dynamically and programmatically compiling-running a job");
    job.setJarByClass(Platform.class);
    //job.setMapperClass(Mapper_Class.class);
    //job.setReducerClass(Reducer_Class.class);

    // key/value of your reducer output
    job.setOutputKeyClass(Text.class);
    job.setOutputValueClass(IntWritable.class);

    FileInputFormat.addInputPath(job, new Path(argsTemp[0]));
    // this deletes possible output paths to prevent job failures
    FileSystem fs = FileSystem.get(conf);
    Path out = new Path(argsTemp[1]);
    fs.delete(out, true);
    // finally set the empty out path
    FileOutputFormat.setOutputPath(job, new Path(argsTemp[1]));

    //job.submit();
    System.exit(job.waitForCompletion(true) ? 0 : 1); 
    System.out.println("Job Finished!");        
} catch (Exception e) { return false; }
return true;
}

Пересмотрено: поэтому я пересмотрел код, чтобы указать преобразователь и редукторы с помощью conf.set("mapreduce.map.class, "my mapper.class"). Теперь код компилируется правильно, но при выполнении выдает следующую ошибку:

24 декабря 2012 г. 6:49:43 org.apache.hadoop.mapred.JobClient monitorAndPrintJob INFO: Идентификатор задачи: попытка_201212240511_0006_m_000001_2, Статус: FAILED java.lang.RuntimeException: java.lang.ClassNotFoundException: Mapper_Reducer_Classesclass$Mapper_Class.Class. apache.hadoop.conf.Configuration.getClass(Configuration.java:809) в org.apache.hadoop.mapreduce.JobContext.getMapperClass(JobContext.java:157) в org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask. java:569) в org.apache.hadoop.mapred.MapTask.run(MapTask.java:305) в org.apache.hadoop.mapred.Child.main(Child.java:170)


person reza    schedule 23.12.2012    source источник


Ответы (3)


Если у вас их нет во время компиляции, то напрямую установите имя в конфигурации следующим образом:

conf.set("mapreduce.map.class", "org.what.ever.ClassName");
conf.set("mapreduce.reduce.class", "org.what.ever.ClassName");
person Thomas Jungblut    schedule 23.12.2012
comment
Вы должны добавить банку Hadoop в свойство с именем tmpjars. Так что это будет работать так: conf.set("tmpjars", "/usr/local/hadoop/hadoop-core.jar,/usr/local/hadoop/hadoop-example.jar). Пути JAR должны быть разделены запятой. Обратите внимание, что это довольно хакерский подход, и вы должны позаботиться о том, чтобы эти jar-файлы действительно находились на клиентской машине (для того, чтобы Hadoop скопировал их в HDFS и загрузил в трекеры задач). - person Thomas Jungblut; 24.12.2012
comment
спасибо Томас. Я понял эту часть, и теперь мой код компилируется правильно. Но когда во время выполнения выдает какую-то ошибку. Я пересмотрел свой первоначальный пост, чтобы отразить это. Есть идеи? - person reza; 24.12.2012
comment
Вы явно добавили свою банку, в которой находится картограф, в tmpjars? - person Thomas Jungblut; 24.12.2012
comment
Я тоже это пробовал. вот варианты, которые я пробовал: 1) Я указал требуемые банки при вызове java (java -cp requierd_JARs) без указания conf.set(tmpjars, ...). это сработало, задание отправлено в Hadoop, и это событие показало ИНФОРМАЦИЯ: карта 0% уменьшает 0%, но затем внезапно выдает java.lang.ClassNotFoundException: Mapper_Reducer_Classes$Mapper_Class.class. org/apache/hadoop/conf/Конфигурация - person reza; 24.12.2012
comment
2) Если я укажу необходимые JAR-файлы, а также использую conf.set(tmpjars,...), он выдает следующую ошибку, как только выполняет System.exit(job.waitForCompletion(true) ? 0: 1);: java. io.FileNotFoundException: Файл не существует: /Users/me/My_Software/hadoop-0.20.2/hadoop-0.20.2-core.jar. Я дважды проверил, и файл действительно существует в моей системе. Я думаю, что основная причина этого в том, что он проверяет этот путь на HDFS. - person reza; 24.12.2012
comment
3) Указание conf.set(tmpjars,...), но без использования java -cp JAR_Class_paths вызывает следующую ошибку, как только вызывается первый метод hadoop (configuration conf = ...): java.lang.NoClassDefFoundError: org /apache/hadoop/conf/Конфигурация - person reza; 24.12.2012
comment
Кажется, что первая попытка имеет лучший прогресс. Как вы думаете, есть ли какая-то проблема с тем, как я определил свой маппер и редьюсер? conf.set(mapreduce.map.class, Mapper_Reducer_Classes$Mapper_Class.class); conf.set(mapreduce.reduce.class, Mapper_Reducer_Classes$Reducer_Class.class); У меня есть файл Mapper_Reducer_Classes.java, в котором определены как Mapper_class, так и Reducer_Class. мне нужно указать здесь расширение .class и должен ли я иметь Mapper_Reducer_Classes$ в начале? - person reza; 24.12.2012
comment
Вы должны указать имя пакета перед вашим классом - person Thomas Jungblut; 24.12.2012
comment
Он определен в пакете по умолчанию (без упаковки). На самом деле, я удалил всю упаковку, чтобы упростить отладку. - person reza; 24.12.2012
comment
Затем распечатайте полное имя класса с помощью методов класса java и установите его там. Также я думаю, что это свойство libjars, а не tmpjar. Извините, на моем планшете нет исходного кода :/ - person Thomas Jungblut; 24.12.2012
comment
спасибо Томас. не могли бы вы уточнить, что вы имеете в виду, распечатав полное имя класса с помощью методов класса java и установив его там - person reza; 24.12.2012
comment
еще раз спасибо, я наконец-то заработал: я удалил .class из конца conf.set() и вернул ему упаковку. и теперь это работает :) conf.set(mapreduce.map.class, org.mypackage.Mapper_Reducer_Classes$Mapper_Class); conf.set(mapreduce.reduce.class, org.mypackage.Mapper_Reducer_Classes$Reducer_Class); - person reza; 24.12.2012
comment
Вы можете получить полное и правильное наименование класса, используя Mapper_Reducer_Classes.class.getName(). Так вы всегда сможете увидеть, что там нужно настроить. Рад, что это работает для вас сейчас. - person Thomas Jungblut; 25.12.2012
comment
Благодарю. Я попытался следовать этому и удалил свои скомпилированные классы и добавил Mapper_Reducer_Classes.class.getName(). К сожалению, это снова сломало мой код, и я больше не могу его исправить. Я думаю, у меня есть некоторые проблемы с тем, как я ссылаюсь на классы. Я вернулся к исходной проблеме и был бы признателен за любую идею. Я создал более подробное объяснение проблемы и разместил его как новый пост: stackoverflow.com/questions/14071496/ - person reza; 28.12.2012

Проблема в том, что TaskTracker не видит классы в вашей локальной jRE.

Я понял это таким образом (проект Maven);

Во-первых, добавьте этот плагин в pom.xml, он создаст jar-файл вашего приложения, включая все jar-файлы зависимостей,

 <build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <filters>
                    <filter>
                        <artifact>*:*</artifact>
                        <excludes>
                            <exclude>META-INF/*.SF</exclude>
                            <exclude>META-INF/*.DSA</exclude>
                            <exclude>META-INF/*.RSA</exclude>
                        </excludes>
                    </filter>
                </filters>
                <finalName>sample</finalName>
                <!-- 
                <finalName>uber-${artifactId}-${version}</finalName>
                -->
            </configuration>
        </plugin>
    </plugins>
  </build>

в исходном коде java добавьте эти строки, они будут включать ваш sample.jar, созданный для target/sample.jar по тегу выше в pom.xml.

      Configuration config = new Configuration();
      config.set("fs.default.name", "hdfs://ip:port");
      config.set("mapred.job.tracker", "hdfs://ip:port");

      JobConf job = new JobConf(config);
      job.setJar("target/sample.jar");

таким образом, ваши средства отслеживания задач могут ссылаться на написанные вами классы, и ClassNotFoundException не произойдет.

person Adrian Seungjin Lee    schedule 26.08.2013
comment
Это лучший ответ. Возможно, вы не захотите использовать затененный jar-файл, который содержит ВСЕ, необходимое для задания hadoop, и хранить все это в пути к классам для внешней java-программы. Могут быть конфликты jar или другие проблемы. Ссылка на затененный jar через путь позволяет абстрагироваться от внешней программы и отправлять его в кластер Hadoop через встроенные API. Вы можете создать другую банку, используемую внешней программой, содержащую только определенные зависимости, необходимые для этой программы. - person Galuvian; 06.01.2014

Вам нужна только ссылка на объект Class для класса, который будет создан динамически. Используйте Class.for name("foo.Mapper") вместо foo.Mapper.class

person Sean Owen    schedule 23.12.2012