Resourcengebundene Ausführung auf Hazelcast Knoten durch Distributed Code Execution

Hazelcast stellt eine leicht zu bedienende API zur Verwaltung von verteilten Collections und Maps zur Verfügung. Die Verteilung der Daten auf die sich im Clusterverbund befindlichen Knoten ist für den Entwickler hierbei transparent.

Es hat sich gezeigt, dass ein Problem einer solchen verteilten Datenhaltung darin besteht, dass die Daten an den verarbeitenden Client übermittelt werden müssen. Bei großen Datenmengen stellt damit das Netzwerk einen Flaschenhals dar. Eine effektivere Methode als eine großen Menge Daten zum Code zu bringen, besteht darin, den Code dorthin zu bringen, wo die Daten vorliegen.

Desweiteren profitieren wir durch eine mögliche parallele Ausführung auf mehreren Knoten.

Eine weitere interessante Möglichkeit eröffnet sich allerdings auch, sollte ein auszuführender Code abhängig von zuvor angeforderten Ressourcen sein. Dies könnte zum Beispiel eine bestimmte Datenbankconnection, Transaktion oder allgemein eine Session sein, welche auf einem bestimmten Knoten in einem Cluster angefordert worden ist und dort gehalten wird.

Das In-Memory-Grid Hazelcast bietet hierfür umfangreiche Möglichkeiten, welche es erlauben, durch eine intuitiv zu bedienende API ohne größere Aufwände eine solche verteilte Ausführung umzusetzen.

Hazelcast nutzt hierfür das Executor Framework und stellt mit dem Distributed Executor Service eine eigene java.util.concurrent.ExecutorService Implementierung zur Verfügung, welche es erlaubt, als java.util.concurrent.Callable und java.util.Runnable implementierte Tasks innerhalb des Clusterverbundes auszuführen.

Die Regeln für die Ausführung kann hierbei bestimmt werden:

  • auf einem bestimmten Knoten
  • einem Knoten, der einen bestimmten Eintrag enthält
  • auf einem beliebigen Knoten, welcher Hazelcast auswählt
  • auf allen Knoten oder nur einer bestimmten Untermenge der Knoten
IExecutorService executorService = hazelcastClient.getExecutorService("default");
Future<String> future = executorService.submitToKeyOwner(myCallable, key);

Diese Funktion können wir uns zunutze machen, um bestimmte Funktionen dort auszuführen, wo wir die benötigten Ressourcen vorfinden.
Eine Session ID kann hierbei zur Identifikation dieser Ressource dienen und in das In-Memory-Grid gestellt werden. Die erste Zuordnung zu einem Knoten übernimmt nun das Grid. Sowohl die Funktion zum Öffnen der Ressourcen, als auch die auszuführende Interaktionen verpacken wir in java.util.concurrent.Callable Tasks. Wir folgen hier also dem Command-Pattern.

public abstract class DistributedCommand implements Callable<String>, Serializable {

  public String call() throws Exception {
    ...
  }
}

executorService.submitToKeyOwner(resourceboundCommand, sessionID); erlaubt uns nun die Funktion auf dem Knoten auszuführen, an welchem die entsprechende Session bekannt ist. Serialisierbare Ressourcen können wir hierbei direkt in dem Grid halten, z.B. in Form einer verteilten Map, nicht serializierbare Ressourcen müssen im Container für die Funktionen natürlich zugreifbar abgelegt werden und durch die ID auffindbar sein.

Short URL for this post: http://wp.me/p4nxik-216
This entry was posted in Java Runtimes - VM, Appserver & Cloud and tagged , , , , , . Bookmark the permalink.

Leave a Reply