Hazelcast para computación distribuída

Hace un tiempo venimos trabajando con Hazelcast en algunos proyectos en Folder IT. Si bien su uso fundamental ha sido tenerlo como servicio de caching compartido en arquitecturas donde había redundancia de server (N-Tomcat detrás de un load balancer), recientemente hemos estado utilizando otros de los servicios que ofrece, entre ellos: ExecutorService para distribuir la ejecución de tareas.

Entre sus principales características, al ser un IMG (In Memory Datagrid), ofrece almacenamiento de datos en forma de listas, colas, clave/valor, etc, en forma redundante ya que replica los datos a otros nodos del cluser basado en la configuración que hayamos hecho. También permite enviar mensajes llamados Topics, ejecutar acciones map/reduce, entre otros. Debajo una lista de links sobre cada una de las áreas más importantes de Hazelcast.

Hazelcast Features

A modo de ejemplo supongamos que tenemos un proyecto Spring, donde Hazelcast es dependencia y además tenemos un bean llamado “hazelinstance”. Se desean enviar emails cuando se den ciertas condiciones en una tarea agendada (Scheduled).

La implementación del ExecutorService de Hazelcast admite tareas que implementen tanto Callable como Runnable. La diferencia básica estará en si necesitamos el resultado de la respuesta de ejecución del método o no. Para este ejemplo crearemos una tarea Runnable, ya que nos interesa sólo que la tarea se ejecute, en algún nodo del cluster.

public class SendMailTask implements Runnable, Serializable {
    private final Email email;

    public SendMailTask(Email mail ) {
        this.email = mail;
    }

    @Override
    public void run() {
        try {
            // simular una actividad
            Thread.sleep( 1000 );
        } catch ( InterruptedException e ) { }
        System.out.println( "Email enviado. ");
    }
}

Con el fin de poder utilizar el ExecutorService, hay que obtenerlo desde la instancia asociada a nuestra aplicación, o pedírselo a un miembro del cluster dependiendo como sea nuestra configuración. Basado en el ejemplo inicial, el seudocódigo sería el siguiente:

@Service
public class ProjectScheduler {

    @Autowired
    HazelcastInstance hazelInstance;

    @Scheduled(initialDelay = 1000 * 60, fixedDelay = 1000 * 60)
    public void sendNotification() {
        IExecutorService executor = hazelInstance.getExecutorService( "exec" );
        // mail = messageService.getMessages();
        executor.execute( new SendMailTask(mail) );
        
    }
}

El nombre del executor es «exec», dicho nombre se lo asignamos en la configuración de hazelcast. Si quisieramos saber qué nodo está ejecutando la tarea SendMailTask, dentro del método run deberíamos tener una referencia a una instancia Hazelcast, para poder obtener de la siguiente manera el Miembro Local: hazelinstance.getCluster().getLocalMember().

<executor-service name="exec">
  <pool-size>1</pool-size>
</executor-service>xml

Para cerrar, utilizar Hazelcast como medio para ejecutar nuestras tareas nos trae varios beneficios, entre los cuales podemos destacar la posibilidad de escalar horizontalmente rápido (basta con agregar nodos al cluster y tendremos más nodos a los cuales delegar tareas), tener redundancia para la ejecución. Aunque vale destacar que aún no tiene disponible un Scheduled Executor Service, es decir la posibilidad de agendar o progamar tareas que se ejecuten en forma distribuida. Hay un ticket abierto para esto desde 2012, pero en 3.7 no tendremos suerte. Quizá debamos esperar a 3.8.

Nota al publicar: En una actualización reciente de su sitio web, Hazelcast.org ha publicado snippets básicos de código de cómo probar los distintos features que ofrece.

Get in Touch