Inleiding tot Akka HTTP

1. Overzicht

In deze zelfstudie leren we met behulp van Akka's Actor & Stream-modellen hoe Akka moet worden ingesteld om een ​​HTTP-API te maken die basis CRUD-bewerkingen biedt.

2. Maven afhankelijkheden

Laten we om te beginnen eens kijken naar de afhankelijkheden die nodig zijn om met Akka HTTP te werken:

 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 

We kunnen de nieuwste versie van deze Akka-bibliotheken natuurlijk vinden op Maven Central.

3. Een acteur creëren

Als voorbeeld zullen we een HTTP-API bouwen waarmee we gebruikersbronnen kunnen beheren. De API ondersteunt twee bewerkingen:

  • een nieuwe gebruiker aanmaken
  • het laden van een bestaande gebruiker

Voordat we een HTTP-API kunnen bieden, we zullen een actor moeten implementeren die de bewerkingen biedt die we nodig hebben:

klasse UserActor breidt AbstractActor uit {privé UserService userService = nieuwe UserService (); static Props props () {retourneer Props.create (UserActor.class); } @Override openbaar Ontvang createReceive () {retour ontvangBuilder () .match (CreateUserMessage.class, handleCreateUser ()) .match (GetUserMessage.class, handleGetUser ()) .build (); } privé FI.UnitApply handleCreateUser () {return createUserMessage -> {userService.createUser (createUserMessage.getUser ()); sender () .tell (nieuwe ActionPerformed (String.format ("Gebruiker% s gemaakt.", createUserMessage.getUser (). getName ())), getSelf ()); }; } privé FI.UnitApply handleGetUser () {return getUserMessage -> {afzender (). tell (userService.getUser (getUserMessage.getUserId ()), getSelf ()); }; }}

In feite breiden we het AbstractActor class en het implementeren van zijn createReceive () methode.

Binnen createReceive (), we zijn inkomende berichttypen toewijzen naar methoden die berichten van het respectieve type afhandelen.

De berichttypen zijn eenvoudige, serialiseerbare containerklassen met enkele velden die een bepaalde bewerking beschrijven. GetUserMessage en heeft een enkel veld gebruikersnaam om de te laden gebruiker te identificeren. CreateUserMessage bevat een Gebruiker object met de gebruikersgegevens die we nodig hebben om een ​​nieuwe gebruiker aan te maken.

Later zullen we zien hoe we inkomende HTTP-verzoeken in deze berichten kunnen vertalen.

Uiteindelijk delegeren we alle berichten aan een Gebruikersservice instantie, die de bedrijfslogica biedt die nodig is voor het beheren van permanente gebruikersobjecten.

Let ook op de rekwisieten() methode. Terwijl de rekwisieten() methode is niet nodig voor uitbreiding AbstractActor, het komt later van pas bij het maken van het ActorSystem.

Bekijk onze inleiding tot Akka-acteurs voor een meer diepgaande discussie over acteurs.

4. HTTP-routes definiëren

Een acteur hebben die het eigenlijke werk voor ons doet, het enige wat we hoeven te doen is een HTTP-API bieden die inkomende HTTP-verzoeken delegeert aan onze actor.

Akka gebruikt het concept van routes om een ​​HTTP-API te beschrijven. Voor elke operatie hebben we een route nodig.

Om een ​​HTTP-server te maken, breiden we de framework-klasse uit HttpApp en implementeer het routes methode:

klasse UserServer breidt HttpApp {privé laatste ActorRef userActor uit; Time-out time-out = nieuwe time-out (Duration.create (5, TimeUnit.SECONDS)); UserServer (ActorRef userActor) {this.userActor = userActor; } @Override openbare route routes () {retourpad ("gebruikers", dit :: postgebruiker) .orElse (pad (segment ("gebruikers"). Slash (longSegment ()), id -> route (getUser (id)) )); } private Route getUser (lange id) {return get (() -> {CompletionStage user = PatternsCS.ask (userActor, nieuwe GetUserMessage (id), time-out) .thenApply (obj -> (Optioneel) obj); return onSuccess (() -> gebruiker, uitgevoerd -> {if (uitgevoerd.isPresent ()) terugkeer voltooid (StatusCodes.OK, uitgevoerd.get (), Jackson.marshaller ()); anders retour compleet (StatusCodes.NOT_FOUND); }); }); } private Route postUser () {retourroute (post (() -> entiteit (Jackson.unmarshaller (User.class), gebruiker -> {CompletionStage userCreated = PatternsCS.ask (userActor, nieuwe CreateUserMessage (gebruiker), time-out) .thenApply (obj -> (ActionPerformed) obj); return onSuccess (() -> userCreated, uitgevoerd -> {retourneer voltooid (StatusCodes.CREATED, uitgevoerd, Jackson.marshaller ());});}))); }} 

Nu is er een behoorlijke hoeveelheid boilerplate hier, maar merk op dat we hetzelfde patroon volgen als voorheen kaartbewerkingen, dit keer als routes. Laten we het een beetje opsplitsen.

Binnen getUser (), verpakken we eenvoudig het inkomende gebruikers-ID in een bericht van het type GetUserMessage en stuur dat bericht door naar onze userActor.

Zodra de acteur het bericht heeft verwerkt, wordt het onSuccess handler wordt genoemd, waarin we compleet het HTTP-verzoek door een antwoord te sturen met een bepaalde HTTP-status en een bepaalde JSON-body. We gebruiken de Jackson marshaller om het antwoord van de acteur te serialiseren in een JSON-string.

Binnen postUser (), we doen de dingen een beetje anders, omdat we een JSON-body verwachten in het HTTP-verzoek. Wij gebruiken de entiteit() methode om de inkomende JSON-body toe te wijzen aan een Gebruiker object voordat u het in een CreateUserMessage en het doorgeven aan onze acteur. Nogmaals, we gebruiken Jackson om in kaart te brengen tussen Java en JSON en vice versa.

Sinds HttpApp verwacht van ons dat we een single leveren Route object combineren we beide routes tot een enkele binnen de routes methode. Hier gebruiken we de pad richtlijn om uiteindelijk het URL-pad op te geven waarop onze API beschikbaar zou moeten zijn.

We binden de route die wordt geboden door postUser () naar het pad / gebruikers. Als het inkomende verzoek geen POST-verzoek is, gaat Akka automatisch naar het of anders branch en verwacht dat het pad is / gebruikers / en de HTTP-methode die GET moet zijn.

Als de HTTP-methode GET is, wordt het verzoek doorgestuurd naar het getUser () route. Als de gebruiker niet bestaat, retourneert Akka HTTP-status 404 (niet gevonden). Als de methode geen POST of GET is, retourneert Akka HTTP-status 405 (methode niet toegestaan).

Voor meer informatie over het definiëren van HTTP-routes met Akka, bekijk de Akka-documenten.

5. Starten van de server

Zodra we een HttpApp implementatie zoals hierboven, kunnen we onze HTTP-server opstarten met een paar regels code:

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

We maken eenvoudig een ActorSystem met een enkele actor van het type UserActor en start de server op localhost.

6. Conclusie

In dit artikel hebben we de basisprincipes van Akka HTTP leren kennen met een voorbeeld dat laat zien hoe u een HTTP-server instelt en eindpunten blootlegt om bronnen te maken en te laden, vergelijkbaar met een REST API.

Zoals gewoonlijk is de hier gepresenteerde broncode te vinden op GitHub.