Você está na página 1de 19

Procesamiento Paralelo con JPPF

(Java Parallel Processing Framework)

Andrés Felipe Galindo González

Estudiante de Ingeniería de Sistemas y Computación

Universidad de Caldas, Manizales

Noviembre de 2010
RESUMEN
Este pequeño escrito comprende información básica sobre arquitecturas y
modelos manejados a nivel de hardware para un procesamiento avanzado de
datos; esto, pasando por la definición y forma de trabajo de un Clúster o un Grid y
de cómo se llega al pensamiento del procesamiento en paralelo. Con ello partimos
hacia el conocimiento de una excelente herramienta que permite desarrollar tal
tipo de aplicaciones aprovechando al máximo las arquitecturas nombradas en
principio, aprendiendo con un ejemplo práctico, la forma de trabajo sobre el JPPF
(JAVA PARALLEL PROCESSING FRAMEWORK) en cuanto a su modelo
conceptual y práctico en el desarrollo de aplicaciones que aprovechan el
procesamiento en paralelo

ABSTRACT
This little written contains basic information about model and architecture handled
at the hardware level for advanced processing of data; that, through the definition
and method of work in a Cluster or in a Grid and how to get a thought of parallel
processing. Thus we left to the knowledge of an excellent tool that allow develop
these kind of applications maximizing the architectures called at the beginning,
learning with a practical example, working with JPPF (JAVA PARALLEL
PROCESSING FRAMEWORK) in terms of its conceptual and practical model
development applications that take advantage of parallel processing
TABLA DE CONTENIDO

1- INTRODUCCIÓN

2- COMPUTACIÓN DISTRIBUIDA
1.1. CLUSTER
1.2. GRID

3- COMPUTACION PARALELA O CONCURRENTE

4- JPPF(JAVA PARALLEL PROCESSING FRAMEWORK)


4.1. REQUISITOS TÉCNICOS
4.2. DESARROLLO DE UNA APLICACIÓN CON JPPF

5- BIBLIOGRAFÍA
INTRODUCCIÓN

La evolución de los sistemas de cómputo tanto en hardware como en software ha


marcado la tendencia que todos los hombres tenemos de no quedar satisfechos
con lo que tenemos, esto también se evidencia en el ámbito del procesamiento; yo
quisiera saber ¿Cuándo vamos a estar satisfechos con la capacidad y (sobre todo)
la velocidad de procesamiento con que cuentan nuestras máquinas? Creo que es
muy complicado saberlo, aun más viendo las mejoras e innovaciones que vienen
avanzando cada mucho más rápido encontrando velocidades superiores en
equipos de un tamaño ínfimo.
Ése afán por encontrar mayor agilidad y eficacia en procesamiento de datos en un
sistema de cómputo es el que ha definido una gran cantidad de métodos
recursivos, con los cuales se puede conseguir un incremento considerable en la
capacidad y velocidad utilizando más de un solo computador. Esta estructura es
utilizada por todos los métodos que presentaremos a continuación
COMPUTACIÓN DISTRIBUIDA

La computación distribuida se ha convertido en el pilar de los sistemas de


computación, no sólo porque permite mayor agilidad en el procesamiento, sino
también en almacenamiento, con ello tenemos además un nivel más de seguridad
en el resguardo de información.

Uno de los modelos de procesamiento que utilizan esta estructura son los Clúster.
Un clúster es una estructura que tiene varios computadores conectados entre sí
en forma de esclavos y administrados por uno que es el maestro, dicha estructura
permite que el computador designado como maestro utilice los recursos de
hardware de los esclavos para efectuar procesos de alto consumo.

La estructura es la siguiente:

Tareas
Recursos

MAESTRO

ESCLAVO ESCLAVO ESCLAVO


ESCLAVO ESCLAVO
La gráfica anterior describe claramente la estructura y funcionamiento de un
clúster.
El maestro divide la tarea que debe procesar en pequeñas tareas y las reparte
entre sus esclavos, asimismo el maestro está consumiendo los recursos de todos
los nodos esclavos conectados. Esta distribución de tareas y división de procesos
los lleva a cabo un software especializado implementado dentro del maestro.

Otro de los métodos que implementan la computación distribuida es el grid.


Un Grid, a diferencia del Clúster, permite conectar equipos y compartir recursos no
centralizados gráficamente, esto es, redes de gran magnitud para el
procesamiento de información, redes implementadas entre diferentes instituciones
en forma de convenio para el desarrollo tecnológico de diferentes empresas,
ciudades y, por lo general, universidades.

Otra de las diferencias con el Clúster es la funcionalidad de los equipos


conectados, ya que, si tenemos un clúster todos los equipos conectados como
esclavos entregará la totalidad de sus recursos a las tareas designadas por el
maestro, es decir, mientras un equipo esté conectado como esclavo no podrá
utilizarse para una tarea diferente. En un Grid la funcionalidad cotidiana del equipo
no se pierde porque esta conexión sólo compromete cierta cantidad de recursos
que el administrador del esclavo designe para compartirlos en la red, los demás
recursos servirán para que el equipo mantenga su normal funcionamiento. Esta
designación arbitraria de recursos por parte de cada equipo conectado al Grid
permite utilizar dispositivos Móviles, PDA’s, portátiles, en fin, cualquier tipo de
equipo de computo que permita direccionar recursos hacia el Grid y hacia su
propia funcionalidad.

El gráfico que defina la estructura que conforma un Grid podría ser el siguiente:
Cada uno de los nodos que a continuación aparecen pueden estar ubicados en
diferentes ciudades y además cada nodo podría ser no solo un equipo sino una
red de dispositivos que trabajan para un mismo fin; es posible también que el Grid
esté compuesto por varios Clúster, con lo cual, cada nodo podría ser un Clúster
ubicado en una región geográfica diferente. El Nodo de Control es el que hará las
veces de maestro y administrará todos los recursos dispuestos por cada uno de
los nodo permitiendo la descentralización del peso de cada proceso ejecutado
dentro dicho nodo. Puede haber la cantidad de nodos que se quiera y se pueda,
hay redes que abarcan toda la extensión de un país y permiten procesamiento
prácticamente ilimitado
Nodo2
... Nodo3

nodo7 Control Nodo4

Nodo6 Nodo1
Nodo5
COMPUTACIÓN PARALELA O CONCURRENTE

Normalmente hemos tenido sistemas secuenciales que permiten la ejecución de


procesos consecutivos utilizando los recursos sólo uno a la vez y, por tanto, las
aplicaciones desarrolladas para este tipo de sistemas se componen de líneas de
código que se ejecutan una después de la otra.

Con la aparición de los procesadores multicore y sistemas operativos multitarea y


multihilo se empezó a pensar en procesamiento en paralelo o concurrente,
permitiendo que diferentes tareas y al mismo utilizaran los recursos de hardware
de un sistema; no obstante aunque el hardware permitiera tener más de un núcleo
para trabajar, las aplicaciones que existían sólo utilizaban el paradigma secuencial
desperdiciando todo el potencial que un procesador de estas características
pudiera ofreces. Las aplicaciones no tenían la capacidad de utilizar todos lo
núcleos que procesador proveía.

El desarrollo de sistemas multitarea permite que desde software se pudiera


conseguir el paralelismo obteniendo concurrencia entre procesos.

Las ventajas obtenidas al conseguir el paralelismo son evidentes en cuanto a


velocidad de procesamiento en cuanto a MLPS(Millones de líneas por segundo)
ejecutadas en un procesador.

Podemos ver la magnitud de lo anterior en los siguientes gráficos:

Aplicaciones que trabajan con el paradigma serial y equipos de hardware


con un solo procesador
APLICACIONES

RESPUESTA DESDE EL PROCESADOR


HARDWARE

PROCESO_2 >> PROCESO_1 >> PROCESO_0


Aplicaciones que trabajan con el paradigma serial y equipos de hardware
con varios procesadores (núcleos)

RESPUESTA DESDE SÓLO UNO DE LOS NÚCLEOS N_0


APLICACIONES

HARDWARE
PROCESO_2 >> PROCESO_1 >> PROCESO_0 N_1

N_..

Se está desperdiciando la capacidad de procesamiento que el hardware puede


ofrecer, ya que las aplicaciones no son capaces de utilizar los demás núcleos del
procesador

Aplicaciones que trabajan con el paradigma paralelo y equipos de hardware


con varios procesadores (núcleos)

RESPUESTA DESDE CUALQUIERA DE LOS NÚCLEOS N_0


APLICACIONES

PROCESO_2 >> PROCESO_1 >> PROCESO_0 HARDWARE


RESPUESTA DESDE CUALQUIERA DE LOS NÚCLEOS
N_1
PROCESO_5 >> PROCESO_4 >> PROCESO_3

RESPUESTA DESDE CUALQUIERA DE LOS NÚCLEOS


N_..
PROCESO_8 >> PROCESO_7 >> PROCESO_6

Obviamente cada núcleo se podría asumir como un nodo diferente dentro de una
configuración en Grid.
JPPF (JAVA PARALLEL PROCESSING FRAMEWORK)

Java Parallel Processing Framework es una plataforma de computación en Grid de


código abierto que permite desarrollar aplicaciones que pueden ejecutarse en
diferentes computadores dividiéndolas en pequeñas partes que se ejecutan
simultáneamente consiguiendo una reducción considerable en los tiempos de
procesamiento.

JPPF posee las siguientes características:

 Un Grid JPPF cargar y ejecutarse en minutos


 Dinámicamente escalable por demanda
 Permite conexión con los principales servidores de aplicaciones J2EE
 Permite conexión con las plataformas de aplicaciones GigaSpaces eXtreme
 Modelo de programación sencillo
 Herramientas gráficas y programables para administración y monitorización
 Tolerancia a fallos y capacidad de auto-reparación
 Nivel excepcional de servicio y Fiabilidad
 Ejemplos totalmente documentados utilizando JPPF en problemas de la
vida real
 Código abierto flexible licenciado por Apache Licence v2.0

JPPF define cada unidad de trabajo como un “job” que está constituido por
“tasks” que son distribuidas entre los nodos del grid para una ejecución en
paralelo.
La arquitectura de JPPF es la siguiente:

Se divide en tres capas:

 Capa del Cliente: provee un API y herramientas de comunicación para


enviar las tareas a ejecutarse en paralelo
 Capa del Servidor: es responsable de la comunicación entre la capa del
cliente y los nodos administrando la cola de ejecución
 Capa de Ejecución: aquí están los nodos que reciben y ejecutan las tareas
asignadas retornando los resultados de dicha ejecución
REQUISITOS TÉCNICOS:

Para desarrollar una aplicación JPPF y que se ejecute sobre un Grid necesitamos
las siguientes herramientas:

 JPPF application template


 JPPF driver
 JPPF node
 JPPF administration console

DESARROLLO DE UNA APLICACIÓN CON JPPF

Lo primero será crear una tarea (task) que es la parte más pequeña que se
ejecutará en un JPPF Grid

Tendremos que crear una subclase de la clase JPPFTask e implementar en ella la


interfaz Rennable para poder implementar el método run()

Como estamos utilizando una plantilla (JPPF Application template) podremos


encontrar en la siguiente ruta: “…/src/org/jppf/application/template” dos
archivos java con los siguientes nombres: “TemplateJPPFTask.java” y
“TemplateApplicationRunner.java”. vamos a abrir el archivo
TemplateJPPFTask.java en un editor de texto cualquiera y encontraremos una
estructura similar a la siguiente:

public class TemplateJPPFTask extends JPPFTask



public void run()
{
// write your task code here.
System.out.println("Hello, this is the node executing a
template JPPF task");

// ...

// eventually set the execution results


setResult("the execution was performed successfully");
}
Ahí podemos observar cómo se crea la clase heredando desde el JPPFTask y
más abajo la implementación por defecto del método run(). Vemos que en el
método run() se imprime un mensaje en consola y por último se pone el resultado
de la ejecución con una cadena de caracteres en el método setResult(), éste
permitirá que luego podamos recuperar valores obtenidos en dicha tarea
anteriormente.

Bueno, así de sencillo es crear una tarea en JPPF, solo debemos implementar
dentro del método run() lo que queramos que realice.

Ahora vamos a crear y ejecutar un trabajo (job) :

En los API’s de JPPF, un trabajo está representado como una instancia de la clase
JPPFJob.

Para que veamos cómo se implementa el job podemos abrir el otro archivo que
mencionábamos anteriormente (TemplateApplicationRunner.java) y nos
ubicamos sobre el método createJob() que se parecerá a lo siguiente:

public JPPFJob createJob() throws Exception


{
// create a JPPF job
JPPFJob job = new JPPFJob();

// give this job a readable unique id that we can use to


// monitor and manage it.
job.setId("Template Job Id");

// add a task to the job.


job.addTask(new TemplateJPPFTask());

// add more tasks here ...

// there is no guarantee on the order of execution of the


tasks,
// however the results are guaranteed to be returned in
the same
// order as the tasks.
return job;
}
Vemos que para crear el trabajo solo debemos llamar al constructor por defecto de
la clase JPPFJob; el método setId() permite definir un identificador único para
nuestro nuevo trabajo y así poder hacer referencia a él cuando lo necesitemos, si
no se define el identificador utilizando este método el sistema asignará uno
aleatorio en forma de cadena Hexadecimal de 32 caracteres.

A nuestro trabajo le podemos agregar la cantidad de tareas que consideremos


necesarias utilizando el método addTask(). Éstas serán las tareas que compongan
el job que estamos definiendo.

Ahora vamos a ejecutar nuestro job. Para ello vamos a ubicarnos en el método
main(String …args) del archivo TemplateApplicationRunner.java y
encontraremos la siguiente sentencia muy importante dentro del bloque try-
cactch:

jppfClient = new JPPFClient();

Con la anterior sentencia inicializaremos el Framework JPPF que leerá el archivo


de configuración, luego establecerá las conexiones de los nodos en que se
ejecutarán los job’s, después establecerá una conexión para el monitoreo y
administración de los servidores conectados y, por último, registrará los listeners
que monitorearán el estado de cada una de las conexiones

Como ya hemos inicializado el framework, lo que debemos hacer en este


momento es instanciar nuestro job runner, crear un trabajo y ejecutarlos así:

// create a runner instance.


TemplateApplicationRunner runner = new
TemplateApplicationRunner();

// Create a job
JPPFJob job = runner.createJob();

// execute a blocking job


runner.executeBlockingJob(job);

Con la última línea estamos llamando al método executeBlockingJob(JPPFJob


job)

A continuación vemos la implementación de dicho método


/**
* Execute a job in blocking mode. The application will be
blocked until the job
* execution is complete.
* @param job the JPPF job to execute.
* @throws Exception if an error occurs while executing the
job.
*/
public void executeBlockingJob(JPPFJob job) throws Exception
{
// set the job in blocking mode.
job.setBlocking(true);

// Submit the job and wait until the results are returned.
// The results are returned as a list of JPPFTask
instances,
// in the same order as the one in which the tasks where
initially added the job.
List<JPPFTask> results = jppfClient.submit(job);

// process the results


for (JPPFTask task: results)
{
// if the task execution resulted in an exception
if (task.getException() != null)
{
// process the exception here ...
}
else
{
// process the result here ...
}
}
}

La primera sentencia asegura que el job iniciará bloqueado

job.setBlocking(true);

Realmente esta sentencia puede omitirse, ya que, el comportamiento por defecto


de un nuevo trabajo está definido como bloqueado en el Framework JPPF

La siguiente línea envía el job al servidor y espera que se ejecute y retorne los
resultados que se irán guardando en la lista results

List<JPPFTask> results = jppfClient.submit(job);


Desde el punto de vista de JPPF un resultado retornado pro un trabajo tiene dos
posibilidades, es o no un error. Por tal motivo encontramos el siguiente bloque que
busca evitar excepciones que se puedan generar al momento de procesar los
resultados entregados por nuestro job

if (task.getException() != null)
{
// process the exception here ...
}
else
{
// process the result here ...
}

Modifiquemos este bloque para que nos muestre un mensaje en caso que ocurra
una excepción o de lo contrario nos muestre en pantalla los resultados obtenidos

if (task.getException() != null)
{
System.out.println("An exception was raised: "
+ task.getException().getMessage());
}
else
{
System.out.println("Execution result: "
+ task.getResult());
}

Por último sólo nos queda ejecutar nuestra aplicación para lo cual necesitaremos
seguir los siguientes pasos:

1- Iniciar el servidor:
Vamos, desde una consola de comandos, al directorio del JPPF-driver y
escribimos “ant”. Veremos en la consola la siguiente información

run:
[echo] starting the JPPF driver
[java] Class Server initialized - listening on port 11111
[java] Client Server initialized - listening on port 11112
[java] Tasks Server initialized - listening on port 11113
[java] JPPF Driver management initialized
[java] JPPF Driver initialization complete

Ahora el servidor está listo para procesar los job’s


2- Iniciar un nodo:
Vamos, desde otra consola de comandos, al directorio del JPPF-node y
escribimos “ant”. Veremos en la consola la siguiente información

run:
[java] JPPFClassLoader.init(): attempting connection to the
class server
[java] JPPFClassLoader.init(): Reconnected to the class
server
[java] PeerNode.init(): Attempting connection to the JPPF
driver
[java] PeerNode.init(): Reconnected to the JPPF driver
[java] Node successfully initialized

Juntos, el servidor y el nodo, componen el más pequeño de los grid que,


con JPPF, se puede tener

3- Ejecutar la aplicación:
Ahora nos ubicamos, igualmente desde otra consola, en el directorio JPPF-
application-template y escribimos “ant”. En este caso ant primero compilará
nuestra aplicación y luego la ejecutará y podremos ver las siguientes líneas
dentro de la consola:

run:
[java] [client: driver-1 (<ip_address>:11198)]
ClassServerDelegate.init():
Attempting connection to the class server
[java] [client: driver-1 (<ip_address>:11198)]
ClassServerDelegate.init():
Reconnected to the class server
[java] [client: driver-1 (<ip_address>:11198)] : Attempting
connection to the JPPF task server
[java] [client: driver-1 (<ip_address>:11198)] : Reconnected
to the JPPF task server
[java] Execution result: the execution was performed
successfully

Donde <ip_address> corresponde a la dirección IP de nuestro computador

Nótese que la última línea es el mensaje que aparecía como resultado de la


tarea ejecutada.
Si nos devolvemos a la consola en donde abrimos el nodo podremos ver
que aparece el mensaje:

[java] Hello, this is the node executing a template JPPF task


Bueno, después de lo anterior aprendimos la forma de crear una tarea,
asignársela a un trabajo y enviarla al servidor para que éste le designara a un
nodo su ejecución. Es una aplicación simple pero en la cual aplicamos los
conceptos de la arquitectura del JPPF como lo vimos anteriormente.

Ahora podremos empezar a probar nuestro JPPF con tareas más y más grandes y
que demanden más de un nodo e implementarlas en alguna de las composiciones
que definimos al principio como Computación Distribuida si lo aplicamos en un
Grid de una buena cantidad de nodos y ejecutamos una aplicación que consuma
bastantes recursos observaremos la magnitud de la funcionalidad este framework.
BIBLIOGRAFÍA

 Recent Developments in Computing and its Applications. M. Afshar


Alam, Tammana Siddiqui & K. R. Seeja
 http://www.scribd.com/doc/6858172/QUE-ESUN-CLUSTER Tomado en
Noviembre 2010
 http://www.jppf.org/about.php Tomado en Noviembre del 2010
 http://www.jppf.org/wiki/index.php?title=A_first_taste_of_JPPF Tomado
en Noviembre 2010

Você também pode gostar