Einführung in Akka HTTP

1. Übersicht

In diesem Tutorial erfahren Sie anhand der Actor & Stream-Modelle von Akka, wie Sie Akka so einrichten, dass eine HTTP-API erstellt wird, die grundlegende CRUD-Operationen bereitstellt.

2. Maven-Abhängigkeiten

Schauen wir uns zunächst die Abhängigkeiten an, die für die Arbeit mit Akka HTTP erforderlich sind:

 com.typesafe.akka akka-http_2.12 10.0.11   com.typesafe.akka akka-stream_2.12 2.5.11   com.typesafe.akka akka-http-jackson_2.12 10.0.11   com.typesafe.akka akka-http-testkit_2.12 10.0.11 test 

Wir können natürlich die neueste Version dieser Akka-Bibliotheken auf Maven Central finden.

3. Erstellen eines Schauspielers

Als Beispiel erstellen wir eine HTTP-API, mit der wir Benutzerressourcen verwalten können. Die API unterstützt zwei Vorgänge:

  • einen neuen Benutzer erstellen
  • Laden eines vorhandenen Benutzers

Bevor wir eine HTTP-API bereitstellen können, müssen wir einen Akteur implementieren, der die erforderlichen Vorgänge bereitstellt:

class UserActor extends AbstractActor { private UserService userService = new UserService(); static Props props() { return Props.create(UserActor.class); } @Override public Receive createReceive() { return receiveBuilder() .match(CreateUserMessage.class, handleCreateUser()) .match(GetUserMessage.class, handleGetUser()) .build(); } private FI.UnitApply handleCreateUser() { return createUserMessage -> { userService.createUser(createUserMessage.getUser()); sender() .tell(new ActionPerformed( String.format("User %s created.", createUserMessage.getUser().getName())), getSelf()); }; } private FI.UnitApply handleGetUser() { return getUserMessage -> { sender().tell(userService.getUser(getUserMessage.getUserId()), getSelf()); }; } }

Grundsätzlich erweitern wir die AbstractActor- Klasse und implementieren ihre createReceive () -Methode.

In createReceive () ordnen wir eingehende Nachrichtentypen Methoden zu, die Nachrichten des jeweiligen Typs verarbeiten.

Die Nachrichtentypen sind einfache serialisierbare Containerklassen mit einigen Feldern, die eine bestimmte Operation beschreiben . GetUserMessage und verfügt über ein einzelnes Feld userId , um den zu ladenden Benutzer zu identifizieren. CreateUserMessage enthält ein Benutzerobjekt mit den Benutzerdaten, die zum Erstellen eines neuen Benutzers erforderlich sind.

Später werden wir sehen, wie eingehende HTTP-Anforderungen in diese Nachrichten übersetzt werden.

Letztendlich delegieren wir alle Nachrichten an eine UserService- Instanz, die die für die Verwaltung persistenter Benutzerobjekte erforderliche Geschäftslogik bereitstellt.

Beachten Sie auch die Methode props () . Während die props () -Methode für die Erweiterung von AbstractActor nicht erforderlich ist , wird sie später beim Erstellen des ActorSystems nützlich sein .

Weitere Informationen zu Schauspielern finden Sie in unserer Einführung zu Akka Actors.

4. Definieren von HTTP-Routen

Wenn wir einen Akteur haben, der die eigentliche Arbeit für uns erledigt, müssen wir nur noch eine HTTP-API bereitstellen, die eingehende HTTP-Anforderungen an unseren Akteur delegiert.

Akka verwendet das Konzept von Routen, um eine HTTP-API zu beschreiben. Für jede Operation benötigen wir eine Route.

Um einen HTTP - Server zu erstellen, erweitern wir die Framework - Klasse HttpApp und die Umsetzung Routen - Methode:

class UserServer extends HttpApp { private final ActorRef userActor; Timeout timeout = new Timeout(Duration.create(5, TimeUnit.SECONDS)); UserServer(ActorRef userActor) { this.userActor = userActor; } @Override public Route routes() { return path("users", this::postUser) .orElse(path(segment("users").slash(longSegment()), id -> route(getUser(id)))); } private Route getUser(Long id) { return get(() -> { CompletionStage
    
      user = PatternsCS.ask(userActor, new GetUserMessage(id), timeout) .thenApply(obj -> (Optional) obj); return onSuccess(() -> user, performed -> { if (performed.isPresent()) return complete(StatusCodes.OK, performed.get(), Jackson.marshaller()); else return complete(StatusCodes.NOT_FOUND); }); }); } private Route postUser() { return route(post(() -> entity(Jackson.unmarshaller(User.class), user -> { CompletionStage userCreated = PatternsCS.ask(userActor, new CreateUserMessage(user), timeout) .thenApply(obj -> (ActionPerformed) obj); return onSuccess(() -> userCreated, performed -> { return complete(StatusCodes.CREATED, performed, Jackson.marshaller()); }); }))); } } 
    

Jetzt gibt es hier eine ganze Menge Boilerplate, aber beachten Sie, dass wir das gleiche Muster wie zuvor bei Kartierungsvorgängen verfolgen , diesmal als Routen. Lassen Sie es uns ein wenig aufschlüsseln.

In getUser () verpacken wir die eingehende Benutzer-ID einfach in eine Nachricht vom Typ GetUserMessage und leiten diese Nachricht an unseren userActor weiter .

Sobald der Akteur die Nachricht verarbeitet hat, wird der onSuccess- Handler aufgerufen, in dem wir die HTTP-Anforderung abschließen, indem wir eine Antwort mit einem bestimmten HTTP-Status und einem bestimmten JSON-Body senden. Wir verwenden den Jackson Marshaller, um die vom Schauspieler gegebene Antwort in eine JSON-Zeichenfolge zu serialisieren.

In postUser () machen wir die Dinge etwas anders, da wir einen JSON-Body in der HTTP-Anfrage erwarten. Wir verwenden die entity () -Methode, um den eingehenden JSON-Body einem User- Objekt zuzuordnen, bevor wir ihn in eine CreateUserMessage einschließen und an unseren Akteur weitergeben. Wieder verwenden wir Jackson, um zwischen Java und JSON abzubilden und umgekehrt.

Da HttpApp uns ein einziges zu schaffen erwartet Strecke Objekt verbinden wir beiden Routen zu einem einzigen in der Routen - Methode. Hier verwenden wir den Weg Richtlinie schließlich der URL - Weg zur Verfügung stellen , an dem unsere API zur Verfügung stehen sollten.

Wir binden die von postUser () bereitgestellte Route an den Pfad / die Benutzer . Wenn es sich bei der eingehenden Anforderung nicht um eine POST-Anforderung handelt, wechselt Akka automatisch in den Zweig orElse und erwartet, dass der Pfad / users / und die HTTP-Methode GET lautet .

Wenn die HTTP-Methode GET ist, wird die Anforderung an die Route getUser () weitergeleitet . Wenn der Benutzer nicht vorhanden ist, gibt Akka den HTTP-Status 404 (Nicht gefunden) zurück. Wenn die Methode weder ein POST noch ein GET ist, gibt Akka den HTTP-Status 405 (Methode nicht zulässig) zurück.

Weitere Informationen zum Definieren von HTTP-Routen mit Akka finden Sie in den Akka-Dokumenten.

5. Starten Sie den Server

Sobald wir eine HttpApp- Implementierung wie oben erstellt haben, können wir unseren HTTP-Server mit ein paar Codezeilen starten:

public static void main(String[] args) throws Exception { ActorSystem system = ActorSystem.create("userServer"); ActorRef userActor = system.actorOf(UserActor.props(), "userActor"); UserServer server = new UserServer(userActor); server.startServer("localhost", 8080, system); }

Wir erstellen einfach ein ActorSystem mit einem einzelnen Akteur vom Typ UserActor und starten den Server auf localhost .

6. Fazit

In diesem Artikel haben wir die Grundlagen von Akka HTTP anhand eines Beispiels kennengelernt, das zeigt, wie ein HTTP-Server eingerichtet und Endpunkte zum Erstellen und Laden von Ressourcen verfügbar gemacht werden, ähnlich wie bei einer REST-API.

Der hier vorgestellte Quellcode ist wie gewohnt auf GitHub zu finden.