Compartir a través de


Big Data

MapReduce sin Hadoop mediante la canalización de ASP.NET

Doug Duerner
Yeon Chang Wang

Descargar el ejemplo de código(VB)

¿Alguna vez ha querido agregar la potencia de MapReduce sobre datos de gran tamaño a sus aplicaciones de smartphone o análisis de datos enriquecidos en la tableta u otro dispositivo pequeño, pero pensó que sería demasiado difícil?

¿Alguna vez ha pensado en transformar su aplicación existente de un solo nodo en un sistema distribuido de forma rápida y sencilla, sin tener que volver a diseñar la aplicación completa?

Estas preguntas son lo que nos impulsó a embarcarnos en una aventura para crear un componente de MapReduce RESTful extremadamente fácil de configurar y usar.

Productos como Hadoop destacan frente a los desafíos que suponen datos de gran tamaño. Hemos creado una solución que sacrifica parte de esa funcionalidad en pro de la simplicidad y agilidad, para que sea más fácil desarrollar aplicaciones de datos de gran tamaño. De este modo, no es necesario ser un experto para tener un sistema en funcionamiento en poco tiempo. La simplicidad de la malla frente a la complejidad de la configuración de Hadoop y la agilidad de nuestra solución frente a la falta de dinamismo de un clúster de Hadoop hacen que sea una propuesta atractiva.

En resumen, hemos creado una infraestructura muy sencilla que puede usar MapReduce, bien para realizar procesamientos computacionalmente intensivos fuera de los nodos "malla", o bien para recopilar los datos fuera de esos nodos, cuyos resultados se correlacionan y juntan en un resultado final que se devuelve al cliente.

Información previa

El servidor web de IIS (con su canalización de ASP.NET) ha demostrado para ser un servidor web de nivel empresarial altamente escalable. Pero estas tecnologías no están limitadas simplemente a servir páginas web y hospedar sitios web. Realmente no hay ningún motivo técnico por el que no pueda usarlas como un mecanismo de canalización de propósito general al que se obtiene acceso mediante HTTP. Los pasos de la canalización de ASP.NET se ejecutan en secuencia (no se mueve al siguiente paso hasta que haya completado el anterior), pero cada paso puede ejecutarse de forma asincrónica en paralelo. El servidor web de IIS puede configurarse para ejecutar varias canalizaciones de ASP.NET (varios w3wp.exe) que atienden solicitudes HTTP.

El uso de la canalización de ASP.NET como una canalización de propósito general (a la que casualmente se tiene acceso mediante HTTP), en lugar de para servir páginas web y hospedar sitios web, podría parecer un poco inusual; pero una canalización de ASP.NET (con pasos de canalización asincrónica) es en realidad muy similar a la canalización de instrucciones de CPU en los microprocesadores (bit.ly/1DifFvO), y la capacidad de tener varios archivos w3wp.exe (con una canalización de ASP.NET en cada w3wp.exe) es bastante similar al diseño superescalar en los microprocesadores (bit.ly/1zMr6KD). Estas similitudes, junto con una escalabilidad probada, hacen que el uso del servidor web de IIS y la canalización de ASP.NET para todo lo que necesita una funcionalidad de canalización sea una propuesta atractiva.

Existen muchos productos que ya realizan MapReduce RESTful (Hadoop, Infinispan, Riak, CouchDB, MongoDB y otros), pero nuestras investigaciones sugieren que son bastante difíciles de configurar o requieren conocimientos especializados.

Nuestra intención es simplemente usar nuestros servidores de IIS de Windows existentes que ya están en funcionamiento, usar nuestros métodos de API que ya están escritos, obtener los datos de nuestras pantallas de la interfaz de usuario bajo demanda y disponer de todo el sistema de MapReduce distribuido en funcionamiento en minutos (y todo ello con conocimientos limitados sobre la arquitectura y el diseño del sistema MapReduce o los sistemas distribuidos). De esta forma, se puede transformar rápida y fácilmente una aplicación de pequeña escala existente en un sistema distribuido mayor con un esfuerzo y conocimientos mínimos, en sus propios servidores o en la nube. O bien, si quiere agregar análisis de datos enriquecidos a una aplicación de smartphone existente, puede hacerlo con un mínimo esfuerzo.

Este componente MapReduce RESTful es un extra que no requiere volver a escribir la aplicación existente y un candidato ideal cuando el objetivo es simplemente agregar funcionalidad distribuida básica a una aplicación existente que ya tiene una amplia API de datos públicos. Es posible emular de forma rápida y sencilla patrones de procesamiento distribuido como "dispersión o recopilación", como se muestra en la Figura 1.

Emulación de patrones de procesamiento distribuido como "dispersión o recopilación"
Figura 1. Emulación de patrones de procesamiento distribuido como "dispersión o recopilación"

El proyecto de ejemplo que se incluye en este artículo ofrece el punto de partida de una infraestructura básica simple que muestra esta filosofía de diseño y se puede expandir más allá. El factor atractivo de este diseño no es que sea mejor que otros productos, sino que es más fácil. Es simplemente una alternativa de diseño fácil de usar para los sistemas MapReduce de grandes empresas que hoy día se usan. El diseño no es en ningún caso un sustituto de los productos maduros de MapReduce para empresas, como Hadoop, y no queremos dar a entender que siquiera se aproximen a tener toda la funcionalidad de los productos líderes.

MapReduce

En términos sencillos, MapReduce es una manera de juntar grandes almacenes de datos. El paso de asignación se ejecuta en muchos nodos de servidores de procesamiento distribuido. Normalmente se ejecuta una tarea en cada nodo del servidor distribuido para recuperar datos de los nodos de datos y puede, opcionalmente, transformar o procesar previamente los datos mientras está todavía en el nodo del servidor distribuido. El paso de reducción se ejecuta en uno o varios nodos del servidor de procesamiento final y consolida todos los resultados de los pasos de asignación en un conjunto final de resultados mediante el uso de muchos algoritmos de combinación diferentes.

En el contexto de una API de objeto comercial, el paso de asignación ejecuta un método de la API del objeto comercial para obtener datos y el paso de reducción combina todos los resultados del paso de asignación en un conjunto de resultados final (mediante una unión por clave principal o una agregación como, por ejemplo, una suma por grupo) que se devuelve al cliente que realizó la solicitud.

Una de las ventajas principales de MapReduce es que permite "escalar horizontalmente" en lugar "verticalmente". En otras palabras, permite simplemente agregar más nodos de servidor normales, en lugar de tener que comprar hardware mejor para poder escalar el nodo de servidor principal. El escalado horizontal es generalmente la opción más barata y flexible porque usa hardware estándar disponible de forma común, mientras que el escalado vertical es normalmente mucho más caro porque el costo del hardware suele aumentar exponencialmente a medida que es más sofisticado.

Como una nota al margen de interés, MapReduce destaca cuando trata con volúmenes de datos extremadamente grandes (a escala de Internet) y los datos están parcialmente estructurados o sin estructurar, como archivos de registro y blobs. En cambio, las bases de datos relacionales de SQL sobresalen cuando existen datos estructurados normalizados con esquemas, al menos hasta un cierto límite a partir del cual la sobrecarga de la base de datos relacional es incapaz de trabajar con la enorme cantidad de datos.

En la Figura 2 se muestra una descripción general a alto nivel del proceso de MapReduce y se compara una consulta de base de datos relacional de SQL simple con la consulta correspondiente en un proceso de MapReduce de datos de gran tamaño.

Consulta de una base de datos relacional SQL simple frente a la misma consulta con MapReduce
Figura 2. Consulta de una base de datos relacional SQL simple frente a la misma consulta con MapReduce

REST

La transferencia de estado representacional (Representational State Transfer, REST) define una API pública que se ejecuta en HTTP y usa un paradigma de creación, lectura, actualización y eliminación (CRUD), que se basa en los verbos de HTTP Post, Get, Put y Delete, para devolver una representación de un objeto desde el servidor al cliente que realizó la solicitud. REST intenta permitir el acceso público al propio objeto como una entidad, no sol como operaciones funcionales sobre el objeto. No se trata de una especificación o una RFC, sino simplemente de una recomendación de diseño. Puede cumplir rigurosamente con el diseño REST puro y requerir que la dirección URL tenga un formato para tratar el objeto como una entidad, de la siguiente manera:

http://server/MapReducePortal/BookSales/Book A

O bien puede optar por un diseño más de estilo RPC y requerir que la dirección URL tenga un formato con los nombres de la clase y el método que se va a ejecutar, como se indica a continuación:

http://server/MapReducePortal/BookSales/GetTotalBookSalesByBookName/Book A
http://server/MapReducePortal/BookSales/GetTotalBookSalesByBookName?bookName=Book A

MapReduce RESTful

MapReduce RESTful significa realizar operaciones de MapReduce sobre HTTP para la API y para el mecanismo de transporte entre los nodos del servidor distribuido.

REST sobre HTTP para la API y el transporte tiene varias ventajas que hacen que sea atractivo:

  • el protocolo HTTP en el puerto 80 es compatible con un firewall.
  • Las aplicaciones de cliente desde casi cualquier plataforma pueden consumir fácilmente recursos sin necesidad de dependencias específicas de la plataforma.
  • Los verbos de HTTP (Get, Post, Put y Delete) son un paradigma sencillo y elegante para solicitar recursos.
  • La compresión gzip puede ayudar a reducir los tamaños de carga.
  • El protocolo HTTP tiene ventajas adicionales, como el almacenamiento en caché integrado.

Actualmente, el proyecto de ejemplo usa REST sobre HTTP para la API y el transporte y admite solo Get y Post, no tiene operaciones de escritura y se comunica completamente en JSON. Es similar a algunas de las metodologías conocidas para tener acceso al sistema de archivos distribuido Hadoop (Hadoop Distributed File System, HDFS) de forma externa (por ejemplo, YARN de Hadoop y WebHDFS de Hadoop), pero solo admite el mínimo necesario para que funcione el sistema. No estamos intentando reemplazar Hadoop o calcar toda su extensa funcionalidad. Simplemente estamos intentando ofrecer una alternativa muy rudimentaria y fácil de usar, a costa de la funcionalidad.

Configuración de MapReduce

Para el proyecto de ejemplo, simplemente copie el archivo MapReduceModule.dll en el directorio \bin del directorio virtual de cada nodo del servidor IIS que quiera usar como un nodo de servidor distribuido en el sistema Map­Reduce y, después, coloque una entrada en la sección de módulos de web.config, así:

<modules>
  <add name="MapReduceModule" type="MapReduce.MapReduceModule" />
</modules>

Eso es todo. Tan fácil como eso.

Si no hay ningún directorio virtual en el nodo del servidor IIS, cree un nuevo directorio virtual con un directorio \bin, conviértalo en una aplicación y asegúrese de que usa un grupo de aplicaciones de Microsoft .NET Framework 4. Aumente el número de procesos de trabajo w3wp.exe en el grupo de aplicaciones que da servicio al directorio virtual MapReducePortal para ofrecer más canalizaciones de procesamiento para las solicitudes de MapReduce. Normalmente, las otras opciones de configuración avanzada que se usan para ajustar el servidor IIS normalmente ya están establecidas por el departamento de TI que administra el servidor y están fuera del ámbito de este artículo; pero si no es el caso, están disponibles en el sitio web de Microsoft.

Configuración de REST

Para el proyecto de ejemplo, simplemente coloque el elemento PathInfoAttribute en cualquiera de los métodos de API de datos de objetos de negocios existentes y especifique la cadena PathInfo que se usará para asignar la dirección URL al método y a los argumentos del método. Eso es todo.

Una de las características interesantes del código de ejemplo es que sean cuales sean los tipos de datos que los métodos de API de datos de objetos de negocios existentes devuelvan actualmente, pueden permanecer esos mismos tipos sin necesidad de cambiarlos. La infraestructura puede controlar casi cualquier tipo automáticamente porque usa un elemento DynamicObject de .NET para representar dinámicamente los tipos de datos devueltos. Por ejemplo, si el método devuelve una colección de objetos Customer, el elemento DynamicObject representa un tipo de datos Customer.

La cadena PathInfo de PathInfoAttribute usa la misma clase UriTemplate de .NET que usa Windows Communication Foundation (WCF) y permite hacer todas las mismas cosas que se pueden hacer en un proyecto REST de HTTP web de WCF o un proyecto de API 2 web de ASP.NET como, por ejemplo, la sustitución de nombres de variables de argumentos, caracteres comodín y otros. Elija qué dirección URL se asigna a qué métodos. Tiene control total y es libre de implementar la API de REST de la forma que quiera. Puede parecerse a una API de REST pura y hacer que los segmentos de dirección URL representen los objetos como entidades de primera clase:

http://server/MapReducePortal/BookSales/Book A
[PathInfoAttribute(PathInfo="/BookSales/{bookName}", ReturnItemType="Book")]
public BookSales GetTotalBookSalesByBookName(string bookName)
{
}

O si lo prefiere, puede separarse de los patrones REST y hacer que los segmentos de dirección URL especifiquen el nombre de clase y del método que quiere ejecutar en los segmentos de dirección URL:

http://server/MapReducePortal/BookSales/GetTotalBookSalesByBookName/Book A
[PathInfoAttribute(PathInfo="/BookSales/GetTotalBookSalesByBookName/{bookName}",
  ReturnItemType="Book")]
public BookSales GetTotalBookSalesByBookName(string bookName)
{
}

La decisión es completamente suya.

Factores atractivos

Uno de los factores de diseño atractivos del proyecto de ejemplo es la escalabilidad que se obtiene mediante la canalización de ASP.NET como una canalización de MapReduce para ejecutar el proceso de MapReduce. Dado que la canalización de ASP.NET funciona de forma secuencial, es adecuada para realizar los pasos de asignación y reducción. Y lo bueno es que aunque la canalización es secuencial y no se moverá al siguiente paso hasta que se haya completado el anterior, cada paso se puede ejecutar de forma asincrónica. Esto permite que la canalización siga recibiendo y procesando nuevas solicitudes de MapReduce aunque la canalización esté bloqueada esperando a que las llamadas de asignación vuelvan desde los otros nodos de servidor distribuido.

Como se muestra en la Figura 3, cada w3wp.exe aloja una canalización de ASP.NET que actúa como una canalización de MapReduce. El grupo de aplicaciones asignado al directorio virtual Map­ReducePortal administra w3wp.exe (el proceso de trabajo IIS). De forma predeterminada, el grupo de aplicaciones tiene un w3wp.exe que procesa nuevas solicitudes entrantes en el directorio virtual, pero se puede configurar con gran facilidad para que tenga tantos elementos w3wp.exe como quiera. Esto le permite tener varias canalizaciones de MapReduce en un único nodo de servidor independiente, todas trabajando en equipo para procesar las solicitudes entrantes de MapReduce en el directorio virtual MapReducePortal. La naturaleza asincrónica de la canalización de ASP.NET individual permite que muchas solicitudes se procesen en paralelo. La capacidad de tener varios w3wp.exe que permiten varias canalizaciones de ASP.NET le lleva al siguiente nivel.

Incremento del número de procesos de trabajo IIS del grupo de aplicaciones para tener más canalizaciones de MapReduce que den servicio a solicitudes de MapReduce enviadas al directorio virtual MapReducePortal de este servidor IIS
Figura 3. Incremento del número de procesos de trabajo IIS del grupo de aplicaciones para tener más canalizaciones de MapReduce que den servicio a solicitudes de MapReduce enviadas al directorio virtual MapReducePortal de este servidor IIS

El diseño del proyecto de ejemplo también le permite seguir agregando tantos servidores IIS como quiera para formar una "malla" cada vez mayor de nodos del servidor, como se muestra en la Figura 4. Potencialmente, cuanto más crezca la malla, mayor será el problema que se puede controlar, ya que se divide en fragmentos cada vez más pequeños para resolverlo, y mayor será el nivel de paralelismo que potencialmente se puede lograr. La canalización de ASP.NET asincrónica, combinada con varias canalizaciones por cada servidor, habilita el paralelismo a través de núcleos de CPU de un único servidor. La malla de servidores ofrece otro nivel de paralelismo entre muchas máquinas de servidor. Es muy sencillo agregar más servidores IIS a la malla, lo único que tiene que hacer es copiar el archivo MapReduceModule.dll en la carpeta \bin del directorio virtual y agregar una entrada al archivo web.config. Dado que los servidores IIS son todos simplemente servidores independientes, no se requiere ninguna configuración adicional. Los productos como Hadoop, por el contrario, requieren generalmente más esfuerzo, planeación y experiencia, porque los servidores normalmente deben configurarse como un servidor "clúster" real.

Cualquier nodo del servidor puede iniciar la solicitud de MapReduce y cualquier número de otros nodos de servidor distribuido que aparezcan en la dirección URL de AJAX pueden ejecutar las partes del paso de asignación de esa solicitud en paralelo
Figura 4. Cualquier nodo del servidor puede iniciar la solicitud de MapReduce y cualquier número de otros nodos de servidor distribuido que aparezcan en la dirección URL de AJAX pueden ejecutar las partes del paso de asignación de esa solicitud en paralelo

Ni siquiera son necesarios servidores IIS creados especialmente. Simplemente puede usar cualquier servidor IIS disponible, sencillamente copiando el archivo MapReduceModule.dll en cualquier directorio virtual que ya esté en el servidor. Eso es todo. La próxima llamada de AJAX ya puede incluir el nuevo servidor IIS en el parámetro de la lista distributednodes en el elemento QueryString de la dirección URL.

Otra ventaja del diseño de malla de servidor es que no depende de un nodo Master para funcionar. En productos como Hadoop, el nodo Master administra el clúster del servidor y la ubicación de los datos a lo largo de ese clúster de servidor. Y ese nodo Master fue el origen del error cuando se escaló Hadoop a su límite en producción y no la cantidad de datos ni la infraestructura.

En este diseño de malla del servidor no hay ningún nodo Master. Cualquier nodo de servidor puede iniciar la solicitud de MapReduce y los datos residirán en el nodo que lo recopile. Como se muestra en la Figura 4, cualquier nodo de servidor puede ser el solicitante de los datos y el proveedor de ellos al mismo tiempo en paralelo. Un servidor puede solicitar datos desde otros nodos de la malla del servidor que va a realizar la función de asignación y puede recibir los resultados, combinarlos en un conjunto de resultados final en el paso de reducción. Al mismo tiempo, ese mismo nodo de servidor también puede actuar como un nodo de servidor perimetral que controla un paso de asignación y devuelve sus resultados parciales de una solicitud de MapReduce que se originó en otro nodo de servidor y se va a reducir en ese nodo.

Actualmente, los clientes que realizan las solicitudes identifican la ubicación de los datos (mediante la lista distributednodes en el elemento QueryString de la dirección URL). En su lugar, podría modificar el diseño para almacenar esta lista (o solo los nodos vecinos más cercanos a este, o los nodos que hospedan datos divididos en varios nodos) en una tabla de base de datos en cada nodo individual y agregarlos mediante programación a la dirección URL en tiempo de ejecución. En cierto modo, esto podría convertir el concepto de nodo Master único en uno de nodo Master distribuido, donde cada nodo sepa de dónde puede obtener sus datos. Sería como si el nodo Master se distribuyera por toda la malla, lo que le permitiría escalarse con la malla.

Dado que este diseño de malla usa una gama de productos de Microsoft totalmente probados (las bases de datos de SQL Server, Windows Server y servidor web de IIS), obtendrá las características robustas de redundancia y tolerancia frente a errores (por ejemplo, equilibrio de carga de red [Network Load Balancing, NLB] en Windows Server para IIS, grupos de disponibilidad AlwaysOn con reparación de página automática o creación de reflejos con reparación de página automática para SQL Server) que ya están integradas en estos productos comerciales. Los detalles de estas características están disponibles en sitios web de Microsoft.

El diseño del proyecto de ejemplo también permite que varias solicitudes de MapReduce se "encadenen" juntas para formar flujos de trabajo en los que la entrada inicial de una solicitud de MapReduce es el resultado de la solicitud anterior de MapReduce. Esto se consigue cambiando la solicitud de MapReduce a Post en lugar de Get e incluyendo los resultados de solicitud anterior de MapReduce en el cuerpo de la solicitud Post. En la Figura 5 se muestra un ejemplo de la salida resultante en la página de prueba.

Visualización de la salida procedente del encadenamiento en una página de prueba
Figura 5. Visualización de la salida procedente del encadenamiento en una página de prueba

Información general del proyecto de ejemplo

En esencia, MapReduceModule.dll transforma la canalización de ASP.NET en una canalización de MapReduce. Usa un elemento HttpModule para implementar las funcionalidades de asignación y reducción. Como una nota al margen de interés, algunas de las operaciones de combinación (como la unión) que se ejecutan durante el paso de reducción se basan en un elemento IEqualityComparer<T>, donde T es un elemento DynamicObject que, en cierto sentido, permite realizar las comparaciones de igualdad en función de un nombre de propiedad como un valor de cadena en tiempo de ejecución, aunque IEqualityComparer<T> requiere que se defina un tipo concreto en tiempo de compilación. Es genial.

La Figura 6 es una visión general de alto nivel del diseño del elemento MapReduce­Module.dll, que muestra el flujo de procesamiento cuando pasa por MapReduceModule.dll. MapReduceModule es el único archivo dll necesario y debe estar en cada nodo de servidor que quiera que participe en la infraestructura de MapReduce. La incorporación de MapReduce­Module.dll en el servidor es muy sencilla y se consigue simplemente copiando el archivo MapReduceModule.dll en la carpeta \bin del directorio virtual y agregando una entrada al archivo web.config.

Visión general del diseño de alto nivel del flujo de proceso de MapReduceModule.dll
Figura 6. Visión general del diseño de alto nivel del flujo de proceso de MapReduceModule.dll

En la Figura 6, la interfaz IHttpModule usa el primer paso de la canalización de ASP.NET para la funcionalidad de asignación, mediante la suscripción al evento AddOnBeginRequestProcessingAsync que se desencadena durante el paso de inicio del procesamiento de solicitudes en la canalización de ASP.NET. El elemento IHttpModule usa el último paso de la canalización de ASP.NET para la funcionalidad de reducción, mediante la suscripción al evento AddOnEndRequestProcessingAsync que se desencadena durante el paso de fin de procesamiento de solicitudes en la canalización de ASP.NET.

En resumen, se suscribe solo a los eventos de inicio del procesamiento de solicitudes y fin del procesamiento de solicitudes en la canalización de ASP.NET. Se ejecutan secuencialmente y no se mueven al siguiente paso hasta que se complete el anterior.

Durante el paso de inicio del procesamiento de solicitudes, la interfaz IHttpModule inicia todas las solicitudes de asignación mediante consultas al nodo local y el envío de una solicitud web HTTP a cada uno de los nodos de servidor distribuido presentes en el parámetro de la lista distributednodes en el elemento QueryString de la dirección URL. La solicitud web HTTP enviada a cada uno de los nodos de servidor distribuido usa la misma dirección URL que inició esta solicitud, pero sin el parámetro distributednodes en su dirección URL.

En los nodos de servidor distribuido que reciben la solicitud de asignación, los mismos dos pasos de canalización de ASP.NET se ejecuten secuencialmente, pero dado que no hay ningún parámetro distributednodes en su dirección URL, los pasos de inicio del procesamiento de solicitudes y fin del procesamiento de solicitudes consultan principalmente solo ese nodo. El método de recuperación de datos de asignación que se especifica con el elemento PathInfoAttribute se ejecuta en ese nodo de servidor distribuido perimetral para obtener los datos locales de ese nodo. Los datos que se devuelven en el flujo de respuesta de cada nodo de servidor distribuido perimetral al nodo de servidor que inició la solicitud original se almacena entonces en el elemento HttpContext, con la dirección URL como clave, de forma que se pueda recuperar más adelante en el paso final de reducción.

En el nodo de servidor que inició la solicitud original, se ejecuta el método de recuperación de datos de asignación especificado con el elemento PathInfoAttribute para obtener los datos locales que se encuentra en el nodo de servidor local que inició la solicitud original. A continuación, los datos del nodo del servidor local se almacenan en el elemento HttpContext con la dirección URL como clave, de forma que se pueda recuperar en el último paso de reducción.

Durante el paso de fin del procesamiento de solicitudes, el elemento IHttpModule ejecuta el paso de reducción mediante una búsqueda en el elemento HttpContext de todos los datos y los parámetros de reducción que se facilitaron en el objeto QueryString de la dirección URL (que puede constar de opciones predefinidas como sum=, union= y sort=, u opciones de función personalizada como reduce=CustomReduceFunction). A continuación, combina o reduce los conjuntos de datos de todos los nodos en un conjunto de resultados final mediante el parámetro de reducción especificado. Por último, serializa el conjunto de resultados final en JSON y devuelve ese conjunto de resultados en el flujo de respuesta al cliente que inició la solicitud de MapReduce de AJAX original. Si no se especifican parámetros de reducción, se devuelven todos los datos sin procesar de todos los nodos. En la Figura 7 se muestra un ejemplo de la salida resultante en la página de prueba.

La salida resultante en una página de prueba
Figura 7. La salida resultante en una página de prueba

Comparación del proyecto de ejemplo con Hadoop

En la Figura 8 se compara la funcionalidad básica de MapReduce en Hadoop y el proyecto de ejemplo.

Figura 8. Una comparación de la funcionalidad básica de MapReduce

Hadoop Proyecto de ejemplo
Función de trabajo de asignación de Java que cuenta las palabras Cualquier método decorado con PathInfoAttribute es como una función de trabajo de asignación
Función de trabajo de reducción de Java que suma los recuentos de palabras La reducción de los parámetros en el elemento QueryString de la dirección URL (como sum=) es como una función de trabajo de reducción que realiza una operación de suma
Interfaz grabable (serialización) [Serializable()] atributo (serialización)
Interfaz WritableComparable (ordenación)

Interfaz IComparer<T> (ordenación)

Interfaz IEqualityComparer<T> (suma, union)

La entrada del trabajo de asignación es un conjunto de pares <clave,valor> y el resultado del trabajo de reducción es un conjunto de pares <clave,valor> Los argumentos de los métodos marcados con PathInfoAttribute son como entradas del trabajo de asignación, y los parámetros de reducción en el elemento QueryString de la dirección URL realizan la operación de reducción y serializan los resultados en JSON como el resultado del trabajo de reducción

Un escenario común en el que destaca MapReduce es en el recuento del número de veces que aparece una palabra concreta en millones de documentos. En la Figura 9 se muestra una comparación de una parte de pseudocódigo básico que implementa los datos de gran tamaño equivalentes al famoso programa de ejemplo "Hola a todos": el "Ejemplo de recuento de palabras". La figura muestra la implementación del código en Java de Hadoop y el correspondiente código de C# que podría usarse para conseguir su equivalente en el proyecto de ejemplo. Tenga en cuenta que este código es simplemente pseudocódigo y no se puede considerar en ningún caso correcto o completo. Se muestra simplemente para ilustrar las posibles formas de conseguir una funcionalidad similar en los dos diseños. En la Figura 10 se muestra la salida resultante en la página de prueba.

Figura 9. Comparación del pseudocódigo del "Ejemplo de recuento de palabras"

Asignación de Hadoop

public void map(LongWritable key, Text value, OutputCollector<Text, IntWritable> output,
  Reporter reporter) throws IOException {
  String line = value.toString();
  StringTokenizer tokenizer = new StringTokenizer(line);
  while (tokenizer.hasMoreTokens()) {
    word.set(tokenizer.nextToken());
    output.collect(word, one);
  }
}

Reducción de Hadoop

public void reduce(Text key, Iterator<IntWritable> values, OutputCollector<Text, IntWritable>
  output, Reporter reporter) throws IOException {
  int sum = 0;
  while(values.hasNext()) {
    sum += values.next().get();
  }
  output.collect(key, new IntWritable(sum));
}

Asignación del proyecto de ejemplo

http://server/.../WordCount/Test.txt?distributednodes=Node1,Node2,Node3&union=Word&sum=Count
[PathInfoAttribute(PathInfo="/WordCount/{fileName}", ReturnItemType="Row")]
public HashSet<Row> GetWordCount(string fileName)
{
  HashSet<Row> rows = new HashSet<Row>();
  byte[] bytes = File.ReadAllBytes(fileName);
  string text = Encoding.ASCII.GetString(bytes);
  string[] words = text.Split(new char[ ]{ ' ', '\r', '\n' });
  foreach(string word in words)
  {
    dynamic row = new Row();
    row["Word"] = word;
    row["Count"] = 1;
  }
  return rows;
}

Reducción del proyecto de ejemplo

http://server/.../WordCount/Test.txt?distributednodes=Node1,Node2,Node3&union=Word&sum=Count

La salida resultante en una página de prueba
Figura 10. La salida resultante en una página de prueba

En la Figura 11 se muestra cómo conseguir la funcionalidad básica de MapReduce en el proyecto de ejemplo. Observe que la entidad del objeto en la dirección URL se asigna al equivalente de la función del paso de asignación con el elemento PathInfoAttribute y que las opciones del parámetro de reducción en en elemento QueryString de la dirección URL, como sum= y reduce=, equivalen a la funcionalidad del paso de reducción similar en Hadoop.

Figura 11. Funcionalidad básica de MapReduce en el proyecto de ejemplo

          (Como la asignación de Hadoop)                    (Como la reducción de Hadoop)

http://server/.../BookSales?distributednodes=Node1,Node2,Node3&union=BookName&sum=Sales
[PathInfoAttribute(PathInfo="/BookSales", ReturnItemType="Book")]
public BookSales GetTotalBookSales()
{
}

          (Como la asignación de Hadoop)                    (Como la reducción de Hadoop)

http://server/.../Alarms?distributednodes=Node1,Node2,Node3&reduce=UnionIfNotDeleted
[PathInfoAttribute(PathInfo="/Alarms", ReturnItemType="Alarm")]
public Alarms GetAlarms()
{
}
private static HashSet<Alarm> UnionIfNotDeleted(HashSet<Alarm> originalData,
  HashSet<Alarm> newData)
{
}

Ejemplos adicionales

En la Figura 12 se muestran otras formas de conseguir la funcionalidad de tipo asignación y reducción, y cómo se asigna la dirección URL RESTful a los métodos. El código de implementación del método se omite en pro de la brevedad. El código se podría implementar de muchas maneras diferentes, entre las que se incluyen: un algoritmo que cuenta palabras, datos de la tabla BookSales en la base de datos de cada librería en una cadena de librerías, un método de API de datos de objetos de negocios que devuelve una colección de clases de objetos de negocios o datos de sensores de ubicaciones distribuidas por todo el país. El límite es su imaginación, diviértase.

Figura 12. Distintas maneras de conseguir MapReduce con el proyecto de ejemplo

Ejemplo

http://server/.../BookSales/Book A?distributednodes=Node1,Node2,Node3&union=BookName&sum=Sales
[PathInfoAttribute(PathInfo="/BookSales/{bookName}", ReturnItemType="Book")]
public BookSales GetTotalBookSales(string bookName)
{
}

Ejemplo

http://server/.../Alarms?distributednodes=Node1,Node2,Node3&union=AlarmID
[PathInfoAttribute(PathInfo="/Alarms", ReturnItemType="Alarm")]
public Alarms GetAlarms()
{
}

Ejemplo

http://server/.../Alarms?distributednodes=Node1,Node2,Node3&reduce=UnionIfNotDeleted
[PathInfoAttribute(PathInfo="/Alarms", ReturnItemType="Alarm")]
public Alarms GetAlarms()
{
}
private static HashSet<Alarm> UnionIfNotDeleted(HashSet<Alarm> originalData,
  HashSet<Alarm> newData)
{
}

Ejemplo

http://server/.../SensorMeasurements/2?distributednodes=Node1,Node2,Node3&union=SensorID
[PathInfoAttribute(PathInfo="/SensorMeasurements/{sensorID}",
  ReturnItemType="SensorMeasurement")]
public SensorMeasurements GetSensorMeasurements(int sensorID)
{
}

Ejemplo

http://server/.../MP3Songs?distributednodes=Node1,Node2,Node3&union=SongTitle
[PathInfoAttribute(PathInfo="/MP3Songs", ReturnItemType=" MP3Song")]
public MP3Songs GetMP3Songs()
{
}

Resumen

En este artículo se presenta una infraestructura básica y simple de funcionalidad de asignación y reducción a la que se puede obtener acceso de forma RESTful sobre HTTP y se puede consumir en un dispositivo pequeño, como una tableta o un smartphone. También analizamos la transformación de una aplicación de nodo único en un sistema distribuido básico.

Hay muchas infraestructuras de MapReduce extensas que realizan casi todo lo imaginable, pero el objetivo y en lo que se ha centrado este artículo es en conseguir un mecanismo básico de MapReduce que sea muy fácil de configurar y usar.

La sencillez de configuración y expansión de nuestra solución le ofrece la posibilidad de probar su pequeña idea (en varios equipos portátiles) y escalar verticalmente a lo grande cuando se ha probado la idea (en tantos servidores como sea necesario).

El proyecto de ejemplo le permite usar los métodos de API de datos de objetos de negocios existentes en el paso de asignación, simplemente mediante la aplicación de un atributo al método que asigna la ruta de acceso de la dirección URL a ese método. También permite controlar el paso de reducción agregando comandos sencillos al elemento QueryString de la dirección URL como, por ejemplo, una operación de combinación (como una unión) sobre los datos en función de una clave principal.

Mediante la aplicación del atributo a los métodos de API de los datos en un objeto de negocios existente y la especificación de un comando de unión basado en un campo de clave principal en la dirección URL, obtendrá un mecanismo sencillo que puede transformar partes de una aplicación de nodo único en un sistema distribuido básico con muy poco esfuerzo, que ofrece la posibilidad de tener una visión global centralizada de todo el sistema distribuido en un solo lugar. Por ejemplo, un objeto de datos de negocios que normalmente recupera solamente los elementos en ese nodo único ahora puede recuperar elementos en varios nodos, combinados según un campo de clave principal en el elemento. Sería posible correlacionar o agregar todos los datos de las oficinas locales bajo demanda y verlos en una pantalla en la sede central.

En los dispositivos pequeños, el "trabajo pesado" se produce en los servidores de IIS de la malla y no en el dispositivo pequeño. Por tanto, como ejemplo, una aplicación de smartphone puede disfrutar del paradigma de MapReduce mediante la realización de una llamada HTTP simple, con un uso mínimo de los recursos del teléfono.


Doug Duerner es un ingeniero de software senior con más de 15 años de experiencia en el diseño e implementación de sistemas a gran escala con las tecnologías de Microsoft. Ha trabajado para varias instituciones bancarias de Fortune 500 y para una compañía de software comercial que diseñó y creó el sistema de administración de redes distribuidas a gran escala que se usa en el departamento de Defense Information Systems Agency (DISA) para su "Global Information Grid" (cuadrícula de información global) y el Departamento de estado (DoS). Es un experto innato, que se ha centrado en todos los aspectos, pero disfruta con los retos técnicos más complejos y exigentes, especialmente aquellos que todos dicen que "no son posibles". Puede ponerse en contacto Duerner en la dirección de correo electrónico coding.innovation@gmail.com.

Yeon-Chang Wang es un ingeniero de software senior con más de 15 años de experiencia en el diseño e implementación de sistemas a gran escala con las tecnologías de Microsoft. También ha trabajado para una institución bancaria de Fortune 500 y para una compañía de software comercial que diseñó y creó el sistema de administración de redes distribuidas a gran escala que se usa en el departamento de Defense Information Systems Agency (DISA) para su "Global Information Grid" y el Departamento de estado (DoS). También ha diseñado e implementado un sistema de certificación de controladores a gran escala para uno de los fabricantes del chips más grandes del mundo. Wang tiene un máster en Informática. Resuelve problemas complejos como quien masca chicle y puede ponerse en contacto con él en la dirección yeon_wang@yahoo.com.

Gracias a los siguientes expertos técnicos de Microsoft por revisar este artículo: Mikael Sitruk y Mark Staveley
Mikael Sitruk es un ingeniero de software senior con más de 17 años de experiencia en el diseño e implementación de sistemas de gran escala con una gran variedad de tecnologías. Antes de Microsoft, trabajó para un proveedor de software de telecomunicaciones líder e implementó varios productos innovadores. Es un apasionado de los sistemas distribuidos, los datos de gran tamaño y el aprendizaje automático. Trabajó varios años con el ecosistema de Hadoop y con las tecnologías No-Sql como Cassandra y HBase. Puede ponerse en contacto con Mikael en la dirección de correo electrónico Mikael.Sitruk@outlook.com

Mark Staveley es un programador senior del equipo Big Compute de Azure. Antes de trabajar con Azure, Mark formaba parte de Microsoft Research (como responsable de la supervisión del programa de procesamiento y administración de datos de gran tamaño) y también formaba parte anteriormente del equipo de generación de código y compiladores de Xbox One (en el área de compatibilidad y rendimiento del motor de juego). Mark obtuvo su licenciatura en Queen’s University, un máster en la University of Waikato y un doctorado en Informática y Química computacional en la Memorial University. Antes de Microsoft, Mark fue investigador en dos de los mayores centros de procesamiento de alto rendimiento de Canadá.