Ein neues asynchrones Framework von SpringSource
Ein neues asynchrones Framework von SpringSource
Node.js hat es vorgemacht: Anwendungen können asynchron entwickelt werden. Durch Frameworks wie Akka oder vert.x sind diese Ansätze auch auf der JVM vertreten. SpringSource betritt nun mit Reactor diesen Markt – da lohnt sich sicher ein erster Blick.
Asynchrone Programmierung ist vor allem eine Lösung für den Umgang mit Wartezeiten auf I/O – sei es beim Zugriff auf externe Systeme oder Datenbanken. Herkömmliche Systeme nutzen für jeden Request einen Thread. Wenn die Verarbeitung auf Ressourcen warten muss, blockiert der Thread, und das Betriebssystem sorgt dafür, dass ein anderer Thread an die Reihe kommt. Dieser Ansatz führt dazu, dass für jede Netzwerkverbindung und jeden Client ein Thread genutzt werden muss. Bei sehr vielen offenen Netzwerkverbindungen kann das zu Problemen führen – beispielsweise, wenn bei WebSocket jeder Browser auf jedem Client ständig eine Verbindung mit dem Server hat. Dann müssen schnell tausende von Threads genutzt werden. Das verbraucht viele Ressourcen und bietet keine besonders gute Performance.
Das asynchrone Programmiermodell funktioniert anders: Der Entwickler schreibt Callbacks. Statt den Thread darauf warten zu lassen, dass die Ressourcen zur Verfügung stehen, wird der Callback aufgerufen, wenn die erforderlichen Ressourcen wieder da sind. Ein Thread kann mehrere Callbacks hintereinander ausführen, wenn die jeweils notwendigen Ressourcen vorhanden sind. Dadurch können sehr viele Netzwerkverbindungen mit sehr wenigen Threads unterstützt werden. Dieses Vorgehen stammt aus dem Reactor Pattern [2], das dem Framework auch den Namen gegeben hat.
Vorab eine Warnung: Reactor [1] ist noch in einer sehr frühen Entwicklungsphase – es kann also ohne Weiteres sein, dass sich noch vieles ändert. Aktuell ist noch nicht einmal der erste Meilenstein erreicht. Für einen ersten Blick reicht der aktuelle Stand aber auf jeden Fall aus.
Das Reactor-Framework hat drei wesentliche Elemente:
Events werden verschickt und behandelt.
Consumer erhalten die Events und können sinnvoll auf sie reagieren.
Der Selector entscheidet, welche Events ein Consumer bekommt.
Listing 1 zeigt ein sehr einfaches Beispiel: Zunächst wird ein Reactor erzeugt. Dort wird dann ein Consumer registriert. Der Selector wird durch ein $ erzeugt. Das ist eine durch jQuery inspirierte abkürzende Schreibweise – es wäre auch möglich, einen Selector mit einem Konstruktor zu erzeugen. Der Selector wählt die Events aus, die „hello“ als Inhalt haben. Am Ende wird dann an den Reactor ein Event übergeben, das asynchron an den Consumer weitergegeben wird, da es dem Selector-Ausdruck entspricht. Die Methode accept() wird also zu einem späteren Zeitpunkt aufgerufen.
Listing 1
Reactor reactor = R.reactor().get();
reactor.on($("hello"), new Consumer<Event<String>>() {
public void accept(Event<String> t) {
// Logik
}
reactor.notify("hello");
Selectors gibt es aktuell für reguläre Ausdrücke, Klassen der Objekte und URI-Templates. Für die Performance des Systems ist es natürlich...