Introducción a Spring Data Hadoop
0. Índice de contenidos.
- 1. Introducción.
- 2. Entorno.
- 3. Configuración del proyecto.
- 4. Job MapReduce.
- 5. Ejecución.
- 6. Conclusiones.
1. Introducción.
Spring dispone de la librería spring-data-hadoop que nos simplifica el desarrollo con Hadoop principalmente a través de una fácil configuración de los elementos necesarios para trabajar con MapReduce y un API sencillo para trabajar con HDFS, Pig, Hive, HBase, etc. Adicionalmente nos proporciona integración con otros proyectos como Spring Integration y Spring Batch proporcionando un entorno completo para crear soluciones Big Data.
En este tutorial vamos a rehacer el Job MapReduce con el API de Hadoop de este tutorial para hacerlo con Spring Data.
Puedes descargarte el código del tutorial desde mi repositorio de github pinchando aquí.
2. Entorno.
El tutorial se ha realizado con el siguiente entorno:
- Ubuntu 12.04 64 bits
- Oracle Java SDK 1.6.0_27
- Apache Hadoop 2.2.0
- Spring Data 2.0.0 RC2
- Apache Maven 3.1.1
3. Configuración del proyecto
Lo primero será crear el proyecto, para eso vamos a utilizar un arquetipo de maven:
1 2 3 4 5 6 7 |
mvn archetype:generate ... 187: remote -> eu.stratosphere:quickstart-scala (-) 188: remote -> eu.vitaliy:java6se-spring3-archetype (Simple spring 3 archetype) 189: remote -> fr.ybonnel:simpleweb4j-archetype (An archetype for SimpleWeb4j.) ... |
De la interminable lista que hay, voy a escoger el 188 que te crea la estructura de un proyecto simple de spring. Rellenamos la información que nos va pidiendo:
1 2 3 4 5 6 7 8 9 10 11 12 |
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 380: 188 ... Define value for property 'groupId': : com.autentia.tutoriales Define value for property 'artifactId': : spring-data-hadoop Define value for property 'version': 1.0-SNAPSHOT: : Define value for property 'package': com.autentia.tutoriales: : Confirm properties configuration: groupId: com.autentia.tutoriales artifactId: spring-data-hadoop version: 1.0-SNAPSHOT package: com.autentia.tutoriales Y: : Y |
3.1 pom.xml
Lo que nos crea nos va a servir como punto de partida pero vamos a ir borrando las cosas que no nos hacen falta y añadiendo la configuración necesaria para trabajar con Spring Data, las librerías que habrá que añadir al pom.xml así como el repositorio de Spring ya que vamos a probar la última versión disponible 2.0.0 release candidate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<dependencies> ... <dependency> <groupid>org.springframework.data</groupid> <artifactid>spring-data-hadoop</artifactid> <version>2.0.0.RC2</version> </dependency> </dependencies> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>http://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> |
También vamos a utilizar el plugin appassembler-maven-plugin para generar un shell script para ejecutar nuestro código más fácilmente.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<plugin> <groupid>org.codehaus.mojo</groupid> <artifactid>appassembler-maven-plugin</artifactid> <version>1.8</version> <configuration> <programs> <program> <mainclass>com.autentia.tutoriales.Init</mainclass> <id>airQuality</id> </program> </programs> </configuration> </plugin> |
3.2 application-context.xml
Para tener el soporte de Spring para trabajar con Hadoop lo primero será configurar la ruta al namenode que tenemos configurada en el ${HADOOP_HOME}/etc/hadoop/core-site.xml. Si aún no tienes instalado Hadoop puedes consultar este tutorial.
En mi caso la configuración es la siguiente:
1 2 3 4 5 6 7 |
<configuration> <property> <name>fs.default.name</name> <value>hdfs://localhost:8020</value> <final>true</final> </property> </configuration> |
En el fichero application-context.xml de Spring añadimos la siguiente configuración:
Fichero application-context.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:hdp="http://www.springframework.org/schema/hadoop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/hadoop http://www.springframework.org/schema/hadoop/spring-hadoop.xsd"> <hdp:configuration> fs.default.name=${fs.default.name} </hdp:configuration> <context:property-placeholder location="classpath:hadoop.properties"> <hdp:job id="airQualityJob" input-path="${input.path}" output-path="${output.path}" jar-by-class="com.autentia.tutoriales.Init" mapper="com.autentia.tutoriales.AirQualityMapper" reducer="com.autentia.tutoriales.AirQualityReducer"> <hdp:job-runner id="airQualityJobRunner" job-ref="airQualityJob" run-at-startup="true"> </hdp:job-runner></hdp:job></context:property-placeholder></beans> |
Fichero hadoop.properties
1 2 3 |
fs.default.name=hdfs://localhost:8020 input.path=input output.path=output |
Con esto le estamos diciendo a Spring que nos cree una instancia de tipo singleton de org.apache.hadoop.mapreduce.Job y lo añada a su contexto. Será nuestro Job que ejecute la tarea MapReduce donde no hay que especificarle más que la clase del Mapper y la del Reduce. Spring ya sabe identificar los tipos de entrada y salida del Mapper y el Reducer por lo que nos ahorra un montón de código que no sirve más que para configurar el Job.
4. Job MapReduce.
Las clases del Job MapReduce encargado de procesar el fichero de entrada y generar el resultado son las mismas que las del tutorial de primeros pasos de MapReduce con Hadoop. En este caso se ha separado un poco el código por legibilidad:
-
Mapper
12345678910111213141516171819202122232425262728package com.autentia.tutoriales;import java.io.IOException;import org.apache.commons.lang.math.NumberUtils;import org.apache.hadoop.io.DoubleWritable;import org.apache.hadoop.io.Text;import org.apache.hadoop.mapreduce.Mapper;public class AirQualityMapper extends Mapper {private static final String SEPARATOR = ";";public void map(Object key, Text value, Context context) throws IOException, InterruptedException {final String[] values = value.toString().split(SEPARATOR);final String co = format(values[1]);final String province = format(values[10]);if (NumberUtils.isNumber(co.toString())) {context.write(new Text(province), new DoubleWritable(NumberUtils.toDouble(co)));}}private String format(String value) {return value.trim();}} -
Reducer
123456789101112131415161718192021222324252627package com.autentia.tutoriales;import java.io.IOException;import java.text.DecimalFormat;import org.apache.hadoop.io.DoubleWritable;import org.apache.hadoop.io.Text;import org.apache.hadoop.mapreduce.Reducer;public class AirQualityReducer extends Reducer {private final DecimalFormat decimalFormat = new DecimalFormat("#.##");public void reduce(Text key, Iterable coValues, Context context) throws IOException, InterruptedException {int measures = 0;double totalCo = 0.0f;for (DoubleWritable coValue : coValues) {totalCo += coValue.get();measures++;}if (measures > 0) {context.write(key, new Text(decimalFormat.format(totalCo / measures)));}}} -
Clase main
Es la clase que carga el contexto de Spring.
123456789package com.autentia.tutoriales;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Init {public static void main(String[] args) {new ClassPathXmlApplicationContext("META-INF/spring/application-context.xml");}}
5. Ejecución.
Los pasos para la ejecución del Job son muy sencillos:
-
Paso 1: empaquetar el proyecto
Para ello ejecutamos el comando que genera el empaquetado del código fuente y a través del plugin appassembler nos generará un ejecutable:
1mvn clean package appassembler:assemble -
Paso 2: ejecutar el código
Una vez generado un shell script bastará con ejecutarlo. Antes habría que subir al HDFS el fichero de entrada:
12hadoop -copyFromLocal input/calidad_del_aire_cyl_1997_2013.csv inputsh target/appassembler/bin/airQuality
Si todo ha ido bien, en el directorio de salida configurado en el hadoop.properties nos habrá dejado el resultado de la ejecución.
6. Conclusiones.
Una vez más la gente de Spring se ha currado una librería para facilitarnos el trabajo a la hora de trabajar con Hadoop eliminándonos la mayoría de la configuración necesaria para montar el Job. Si te resultaba tediosa la configuración y la forma de trabajar con Hadoop, mediante Spring Data este trabajo parecerá un juego de niños.
Puedes descargarte el código del tutorial desde mi repositorio de github pinchando aquí.
Espero que te haya sido de ayuda.
Un saludo.
Juan