"Mit bibliotek er i klassestien, men jeg får stadig en Class Not Found-undtagelse i et MapReduce-job" - Hvis du har dette problem, er denne blog noget for dig.
Java kræver, at tredjeparts- og brugerdefinerede klasser er på kommandolinjens "-classpath ” mulighed, når JVM er lanceret. `hadoop` wrapper shell-scriptet gør præcis dette for dig ved at bygge klassestien fra kernebibliotekerne i /usr/lib/hadoop-0.20/ og /usr/lib/hadoop-0.20/lib/ mapper. Men med MapReduce udføres dit jobs opgaveforsøg på eksterne noder. Hvordan fortæller du en fjernmaskine til at inkludere tredjeparts- og brugerdefinerede klasser?
MapReduce-job udføres i separate JVM'er på TaskTrackers, og nogle gange skal du bruge tredjepartsbiblioteker i kortet/reducere opgaveforsøg. For eksempel vil du måske have adgang til HBase fra dine kortopgaver. En måde at gøre dette på er at pakke hver klasse, der bruges i den indsendelige JAR. Du bliver nødt til at pakke den originale hbase-.jar
ud og ompak alle klasserne i din indsendelige Hadoop-krukke. Ikke godt. Gør ikke dette:Problemer med versionskompatibilitet kommer til at bide dig før eller siden.
Der er bedre måder at gøre det samme på ved enten at placere din jar i distribueret cache eller installere hele JAR på Hadoop noderne og fortælle TaskTrackers om deres placering.
1. Inkluder JAR i "-libjars ” kommandolinjemulighed for kommandoen `hadoop jar …`. Krukken vil blive placeret i distribueret cache og vil blive gjort tilgængelig for alle opgavens opgaveforsøg. Mere specifikt finder du JAR i en af ${mapred.local.dir}/taskTracker/archive/${user.name}/distcache/… undermapper på lokale noder. Fordelen ved den distribuerede cache er, at din jar muligvis stadig er der ved din næste programkørsel (i det mindste i teorien:Filerne bør kun sparkes ud af den distribuerede cache, når de overskrider den bløde grænse defineret af local.cachen) .størrelse konfigurationsvariabel, er standard til 10 GB, men din faktiske kilometertal kan variere, især med de nyeste sikkerhedsforbedringer). Hadoop holder styr på ændringerne af de distribuerede cache-filer ved at undersøge deres ændringstidsstempel.
*Opdatering til indlæg:Bemærk, at punkt 2 og 3 nedenfor er forældet fra CDH4 og vil ikke længere blive understøttet fra CDH5.
2. Inkluder den refererede JAR i lib-undermappen af den indsendelige JAR:Et MapReduce-job vil udpakke JAR'en fra denne undermappe til ${mapred.local.dir}/taskTracker/${user.name}/jobcache/$ jobid/krukker på TaskTracker-noderne og peg dine opgaver til denne mappe for at gøre JAR tilgængelig for din kode. Hvis JAR'erne er små, skifter de ofte og er jobspecifikke, er dette den foretrukne metode.
3. Endelig kan du installere JAR'en på klyngens noder. Den nemmeste måde er at placere JAR i $HADOOP_HOME/lib mappe, da alt fra denne mappe er inkluderet, når en Hadoop-dæmon starter. Men da du ved, at kun TaskTrackers vil have brug for disse den nye JAR, er en bedre måde at ændre HADOOP_TASKTRACKER_OPTS-indstillingen i hadoop-env.sh-konfigurationsfilen. Denne metode foretrækkes, hvis JAR'en er bundet til koden, der kører på noderne, såsom HBase.
HADOOP_TASKTRACKER_OPTS="-classpath<colon-separated-paths-to-your-jars>"
Genstart TastTrackers, når du er færdig. Glem ikke at opdatere krukken, når den underliggende software ændres.
Alle de ovenstående muligheder påvirker kun koden, der kører på de distribuerede noder. Hvis din kode, der starter Hadoop-jobbet, bruger det samme bibliotek, skal du også inkludere JAR i miljøvariablen HADOOP_CLASSPATH:
HADOOP_CLASSPATH="<colon-separated-paths-to-your-jars>"
Bemærk, at startende med Java 1.6 classpath kan pege på mapper som "/path/to/your/jars/* ” som vil hente alle JAR'er fra den givne mappe.
De samme vejledende principper gælder for native kodebiblioteker, der skal køres på noderne (JNI eller C++ rør). Du kan placere dem i distribueret cache med "-filer ”-indstillinger, inkludere dem i arkivfiler, der er angivet med “-arkiver ” mulighed, eller installer dem på klynge noderne. Hvis den dynamiske bibliotekslinker er konfigureret korrekt, skal den oprindelige kode gøres tilgængelig for dine opgaveforsøg. Du kan også ændre miljøet for jobbets kørende opgaveforsøg eksplicit ved at angive JAVA_LIBRARY_PATH eller LD_LIBRARY_PATH variabler:
hadoop jar <your jar> [main class] -D mapred.child.env="LD_LIBRARY_PATH=/path/to/your/libs" ...