В интернете есть довольно большое количество примеров о том, как запустить MapReduce из стенделон приложения на джаве.
Но начинающему работать с индийским слоником может быть сложно понять, как запустить джобу из какого-нибудь java контейнера.
Например, в этом туториале, любезно предоставленном ikrumping, содержится такой пример кода:
Такой код будет работать, если вы запускаете стенделон приложение:
Если же вы запускаете код из JBOSS AS, WebSphere AS, Glassfish AS и тд, то этот код работать не будет.
Почему? Да потому, что контейнер распаковывает ваш JAR файл в разные свои кеши и запускает классы уже оттуда.
Для этого нужно выполнить следующее:
В вышеприведенном примере нужно заменить:
на
Где первый параметр метода addFileToClassPath содержит путь к JAR файлу внутри распределенной файловой системы HDFS.
А второй — конфигурацию хадупа (org.apache.hadoop.conf.Configuration).
Раньше было еще 2 способа подсунуть свою джарку хадупу, но они уже устарели: blog.cloudera.com/blog/2011/01/how-to-include-third-party-libraries-in-your-map-reduce-job
Но начинающему работать с индийским слоником может быть сложно понять, как запустить джобу из какого-нибудь java контейнера.
Например, в этом туториале, любезно предоставленном ikrumping, содержится такой пример кода:
Job job = new Job(config, "grep"); /* * Для запуска программы из jar-файла необходимо указать любой * класс из вашего приложения. */ job.setJarByClass(Grep.class);
Такой код будет работать, если вы запускаете стенделон приложение:
Если же вы запускаете код из JBOSS AS, WebSphere AS, Glassfish AS и тд, то этот код работать не будет.
Почему? Да потому, что контейнер распаковывает ваш JAR файл в разные свои кеши и запускает классы уже оттуда.
Кому интересно, почему метод setJarByClass не работает в случае контейнера - приглашаю под спойлер
Для начала предлагаю взглянуть на имплементацию метода setJarByClass.
Как видите, метод findContainingJar ожидает, что тип протокола у URL будет «jar».
А в случае каждого каждого контейнера протокол будет свой.
Как результат: метод setJarByClass работает в основном только для стенделон приложений.
public void setJarByClass(Class cls) { String jar = findContainingJar(cls); if (jar != null) setJar(jar); } private static String findContainingJar(Class my_class) { ClassLoader loader = my_class.getClassLoader(); String class_file = my_class.getName().replaceAll("\\.", "/") + ".class"; try { Enumeration itr = loader.getResources(class_file); while (itr.hasMoreElements()) { URL url = (URL)itr.nextElement(); if ("jar".equals(url.getProtocol())) { String toReturn = url.getPath(); if (toReturn.startsWith("file:")) { toReturn = toReturn.substring("file:".length()); } toReturn = toReturn.replaceAll("\\+", "%2B"); toReturn = URLDecoder.decode(toReturn, "UTF-8"); return toReturn.replaceAll("!.*$", ""); } } } catch (IOException e) { throw new RuntimeException(e); } return null; }
Как видите, метод findContainingJar ожидает, что тип протокола у URL будет «jar».
А в случае каждого каждого контейнера протокол будет свой.
Как результат: метод setJarByClass работает в основном только для стенделон приложений.
Как же запустить мапредьюс джобу универсальным способом, не зависящим от конкретного контейнера приложений?
Для этого нужно выполнить следующее:
- создать отдельный JAR, содержащий все классы, используемые из джобы
- зааплодить его в файловую систему HDFS худупа, где вы собираетесь запускать MapReduce
- добавить JAR архив в classpath запускаемой джобы
В вышеприведенном примере нужно заменить:
job.setJarByClass(Grep.class);
на
DistributedCache.addFileToClassPath("/user/UserName/test.jar", config);
Где первый параметр метода addFileToClassPath содержит путь к JAR файлу внутри распределенной файловой системы HDFS.
А второй — конфигурацию хадупа (org.apache.hadoop.conf.Configuration).
Раньше было еще 2 способа подсунуть свою джарку хадупу, но они уже устарели: blog.cloudera.com/blog/2011/01/how-to-include-third-party-libraries-in-your-map-reduce-job
