В интернете есть довольно большое количество примеров о том, как запустить 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