Gids voor reactieve microservices met Lagom Framework

1. Overzicht

In dit artikel verkennen we het Lagom-framework en een voorbeeldtoepassing implementeren gebruikmakend van een reactieve microservices-gestuurde architectuur.

Simpel gezegd, reactieve softwaretoepassingen zijn afhankelijk van berichtgestuurde asynchrone communicatie en zijn in hoge mate Snel reagerend, Veerkrachtig en Elastisch in de natuur.

Met microservice-gedreven architectuur bedoelden we het opsplitsen van het systeem in grenzen tussen samenwerkingsservices om doelen te bereiken van Isolatie, Autonomie, Enkele verantwoordelijkheid, Mobiliteit, enz. Voor meer informatie over deze twee concepten, zie The Reactive Manifesto en Reactive Microservices Architecture.

2. Waarom Lagom?

Lagom is een open source framework dat is gebouwd met het oog op de verschuiving van monolieten naar microservices-gestuurde applicatiearchitectuur. Het abstraheert de complexiteit van het bouwen, uitvoeren en bewaken van door microservices aangedreven applicaties.

Achter de schermen gebruikt het Lagom-framework het Play Framework, een Akka-berichtgestuurde runtime, Kafka voor het ontkoppelen van services, Event Sourcing en CQRS-patronen, en ConductR-ondersteuning voor het bewaken en schalen van microservices in de containeromgeving.

3. Hallo wereld in Lagom

We zullen een Lagom-applicatie maken om een ​​begroetingsverzoek van de gebruiker af te handelen en terug te sturen met een begroetingsbericht samen met weerstatistieken voor de dag.

En we zullen twee afzonderlijke microservices ontwikkelen: Groet en Weer.

Groet zal zich concentreren op het afhandelen van een begroetingsverzoek, in interactie met de weerservice om terug te antwoorden naar de gebruiker. De Weer microservice verzorgt de aanvraag voor weerstatistieken voor vandaag.

In het geval van een bestaande gebruiker die interactie heeft met Groet microservice, wordt het andere begroetingsbericht aan de gebruiker getoond.

3.1. Vereisten

  1. Installeren Scala (we gebruiken momenteel versie 2.11.8) vanaf hier
  2. Installeren sbt build-tool (we gebruiken momenteel 0.13.11) vanaf hier

4. Projectconfiguratie

Laten we nu snel kijken naar de stappen om een ​​werkend Lagom-systeem op te zetten.

4.1. SBT Build

Maak een projectmap lagom-hallo-wereld gevolgd door het buildbestandbuild.sbt. Een Lagom-systeem bestaat meestal uit een set van sbt bouwt met elke build die overeenkomt met een groep gerelateerde services:

organisatie in ThisBuild: = "com.baeldung" scalaVersion in ThisBuild: = "2.11.8" lagomKafkaEnabled in ThisBuild: = false lazy val greetingApi = project ("greeting-api"). instellingen (versie: = "1.0-SNAPSHOT", libraryDependencies ++ = Seq (lagomJavadslApi)) lazy val greetingImpl = project ("greeting-impl") .enablePlugins (LagomJava) .settings (versie: = "1.0-SNAPSHOT", libraryDependencies ++ = Seq (lagomJavadslPersistenceCassandra)) .dependsOn ( greetingApi, weatherApi) luie val weatherApi = project ("weather-api") .settings (versie: = "1.0-SNAPSHOT", libraryDependencies ++ = Seq (lagomJavadslApi)) luie val weatherImpl = project ("weather-impl"). enablePlugins (LagomJava) .settings (versie: = "1.0-SNAPSHOT") .dependsOn (weatherApi) def project (id: String) = Project (id, base = file (id))

Om te beginnen hebben we de organisatiegegevens gespecificeerd, scala versie en uitgeschakeld Kafka voor het huidige project. Lagom volgt een conventie van twee afzonderlijke projecten voor elke microservice: API-project en een implementatieproject.

Het API-project bevat de service-interface waarvan de implementatie afhankelijk is.

We hebben afhankelijkheden toegevoegd aan de relevante Lagom-modules zoals lagomJavadslApi, lagomJavadslPersistenceCassandra voor het gebruik van de Lagom Java API in onze microservices en het opslaan van gebeurtenissen met betrekking tot de persistente entiteit in Cassandra, respectievelijk.

Ook de groet-impl project hangt af van de weer-api project om weerstatistieken op te halen en weer te geven terwijl u een gebruiker begroet.

Ondersteuning voor de Lagom-plug-in wordt toegevoegd door een map met plug-ins te maken met plugins.sbt het dossier, een vermelding hebben voor de Lagom-plug-in. Het biedt alle nodige ondersteuning voor het bouwen, uitvoeren en implementeren van onze applicatie.

Ook de sbteclipse plug-in zal handig zijn als we Eclipse IDE gebruiken voor dit project. De onderstaande code toont de inhoud voor beide plug-ins:

addSbtPlugin ("com.lightbend.lagom"% "lagom-sbt-plugin"% "1.3.1") addSbtPlugin ("com.typesafe.sbteclipse"% "sbteclipse-plugin"% "3.0.0")

Creëer project / build.properties bestand en specificeer sbt versie te gebruiken:

sbt.version = 0.13.11

4.2. Projectgeneratie

Rennen sbt commando vanuit de projectroot zal de volgende projectsjablonen genereren:

  1. groet-api
  2. groet-impl
  3. weer-api
  4. weer-impl

Voordat we beginnen met het implementeren van de microservices, voegen we het src / main / java en src / main / java / resources mappen in elk van de projecten, om de Maven-achtige lay-out van de projectmap te volgen.

Ook worden binnen twee dynamische projecten gegenereerd project-root / target / lagom-dynamic-projects:

  1. lagom-internal-meta-project-cassandra
  2. lagom-internal-meta-project-service-locator

Deze projecten worden intern door Lagom gebruikt.

5. Service-interface

In de groet-api project specificeren we de volgende interface:

openbare interface GreetingService breidt Service {public ServiceCall handleGreetFrom (String-gebruiker) uit; @Override default Descriptor descriptor () {return named ("greetingservice") .withCalls (restCall (Method.GET, "/ api / greeting /: fromUser", this :: handleGreetFrom)) .withAutoAcl (true); }}

GroetService bloot handleGreetFrom () om het begroetingsverzoek van de gebruiker af te handelen. EEN ServiceCall API wordt gebruikt als het retourtype van deze methoden. ServiceCall heeft twee typeparameters Verzoek en Reactie.

De Verzoek parameter is het type van het inkomende verzoekbericht, en de Reactie parameter is het type van het uitgaande antwoordbericht.

In het bovenstaande voorbeeld gebruiken we geen verzoekpayload, het verzoektype is Niet gebruikt, en Reactie type is een Draad begroeting.

GroetService specificeert ook een mapping naar het daadwerkelijke transport dat tijdens de aanroep wordt gebruikt, door een standaardimplementatie van de Service.descriptor () methode. Een service met de naam groetservice wordt geretourneerd.

handleGreetFrom () serviceaanroep wordt in kaart gebracht met behulp van een rust-ID: KRIJGEN methode type en pad-ID / api / groet /: fromUser toegewezen aan handleGreetFrom () methode. Bekijk deze link voor meer informatie over service-ID's.

Op dezelfde manier definiëren we WeatherService interface in het weer-api project. weatherStatsForToday () methode en descriptor () methode spreekt voor zich:

openbare interface WeatherService breidt Service {public ServiceCall weatherStatsForToday () uit; @Override default Descriptor descriptor () {return named ("weatherservice") .withCalls (restCall (Method.GET, "/ api / weather", this :: weatherStatsForToday)) .withAutoAcl (true); }};

WeatherStats wordt gedefinieerd als een opsomming met voorbeeldwaarden voor verschillende weersomstandigheden en willekeurig opzoeken om de weersvoorspelling voor de dag te retourneren:

openbare opsomming WeatherStats {STATS_RAINY ("Going to Rain, Take Umbrella"), STATS_HUMID ("Going to Rain, Take Water"); openbare statische WeatherStats forToday () {return VALUES.get (RANDOM.nextInt (SIZE)); }}

6. Lagom Persistence - Event Sourcing

Simpel gezegd, in een systeem dat gebruik maakt van Sourcing van evenementen, kunnen we alle wijzigingen vastleggen als onveranderlijke domeingebeurtenissen die de een na de ander worden toegevoegd. De huidige status wordt afgeleid door gebeurtenissen opnieuw af te spelen en te verwerken. Deze operatie is in wezen een vouw naar links werking die bekend is uit het functionele programmeerparadigma.

Event sourcing helpt om hoge schrijfprestaties te bereiken door de events toe te voegen en updates en verwijderingen van bestaande events te vermijden.

Laten we nu eens kijken naar onze persistente entiteit in het begroeting-impl-project, Groetentiteit:

public class GreetingEntity breidt PersistentEntity uit {@Override public Behavior initialBehavior (optionele snapshotState) {BehaviorBuilder b = newBehaviorBuilder (nieuwe GreetingState ("Hallo")); b.setCommandHandler (ReceivedGreetingCommand.class, (cmd, ctx) -> {String fromUser = cmd.getFromUser (); String currentGreeting = state (). getMessage (); return ctx.thenPersist (nieuwe ReceivedGreetingEvent (fromUser), evt -> ctx.reply (currentGreeting + fromUser + "!"));}); b.setEventHandler (ReceivedGreetingEvent.class, evt -> state (). withMessage ("Hallo weer")); retourneer b.build (); }}

Lagom biedt PersistentEntity API voor het verwerken van inkomende gebeurtenissen van het type Opdracht via setCommandHandler () methoden en persistente statusveranderingen als gebeurtenissen van het type Evenement. De status van het domeinobject wordt bijgewerkt door de gebeurtenis toe te passen op de huidige status met behulp van de setEventHandler () methode. Het initiële gedrag () abstracte methode definieert de Gedrag van de entiteit.

In initialBehavior (), we bouwen origineel Groetstaat "Hallo" tekst. Dan kunnen we een ReceivedGreetingCommand command handler - die een ReceivedGreetingEvent Gebeurtenis en wordt vastgehouden in het gebeurtenislogboek.

Groetstaat wordt herberekend naar "Hallo weer" door de ReceivedGreetingEvent event handler methode. Zoals eerder vermeld, roepen we geen setters aan - in plaats daarvan maken we een nieuw exemplaar van Staat van de huidige gebeurtenis die wordt verwerkt.

Lagom volgt de conventie van GroetCommand en GroetEvent interfaces om alle ondersteunde opdrachten en gebeurtenissen bij elkaar te houden:

openbare interface GreetingCommand breidt Jsonable uit {@JsonDeserialize openbare klasse ReceivedGreetingCommand implementeert GreetingCommand, CompressedJsonable, PersistentEntity.ReplyType {@JsonCreator openbare ReceivedGreetingCommand (String fromUser) {this.fromUser = Preconditions.checkNotNull vanUser), "); }}}
openbare interface GreetingEvent breidt Jsonable uit {klasse ReceivedGreetingEvent implementeert GreetingEvent {@JsonCreator openbare ReceivedGreetingEvent (String fromUser) {this.fromUser = fromUser; }}}

7. Service-implementatie

7.1. Groet service

openbare klasse GreetingServiceImpl implementeert GreetingService {@Inject openbare GreetingServiceImpl (PersistentEntityRegistry persistentEntityRegistry, WeatherService weatherService) {this.persistentEntityRegistry = persistentEntityRegistry; this.weatherService = weatherService; persistentEntityRegistry.register (GreetingEntity.class); } @Override openbare ServiceCall handleGreetFrom (String-gebruiker) {retourverzoek -> {PersistentEntityRef ref = persistentEntityRegistry.refFor (GreetingEntity.class, gebruiker); CompletableFuture greetingResponse = ref.ask (nieuwe ReceivedGreetingCommand (gebruiker)) .toCompletableFuture (); CompletableFuture todaysWeatherInfo = (CompletableFuture) weatherService .weatherStatsForToday (). Invoke (); probeer {return CompletableFuture.completedFuture (greetingResponse.get () + "Weerstatistieken van vandaag:" + todaysWeatherInfo.get (). getMessage ()); } catch (InterruptedException | ExecutionException e) {return CompletableFuture.completedFuture ("Sorry een fout aan onze kant, bezig eraan te werken"); }}; }}

Simpel gezegd, we injecteren de PersistentEntityRegistry en WeatherService afhankelijkheden met @Injecteren (geleverd door Guice framework), en we registreren het persistent Groetentiteit.

De handleGreetFrom () implementatie is verzenden ReceivedGreetingCommand naar de Groetentiteit om begroetingsreeks asynchroon te verwerken en te retourneren met CompletableFuture invoer van Voltooiingsfase API.

Op dezelfde manier doen we een asynchrone oproep naar Weer microservice om weerstatistieken voor vandaag op te halen.

Ten slotte voegen we beide outputs samen en retourneren we het eindresultaat aan de gebruiker.

Om een ​​implementatie van de service descriptor-interface te registreren GroetService met Lagom, laten we creëren GroetServiceModule klasse die zich uitstrekt AbstractModule en werktuigen ServiceGuiceSupport:

public class GreetingServiceModule breidt uit AbstractModule implementeert ServiceGuiceSupport {@Override protected void configure () {bindServices (serviceBinding (GreetingService.class, GreetingServiceImpl.class)); bindClient (WeatherService.class); }} 

Ook maakt Lagom intern gebruik van het Play Framework. En dus kunnen we onze module toevoegen aan de lijst met ingeschakelde modules van Play in src / main / resources / application.conf het dossier:

play.modules.enabled + = com.baeldung.lagom.helloworld.greeting.impl.GreetingServiceModule

7.2. Weerdienst

Na het bekijken van de GroetServiceImpl, WeatherServiceImpl is vrij eenvoudig en spreekt voor zich:

openbare klasse WeatherServiceImpl implementeert WeatherService {@Override public ServiceCall weatherStatsForToday () {return req -> CompletableFuture.completedFuture (WeatherStats.forToday ()); }}

We volgen dezelfde stappen als hierboven voor de begroetingsmodule om de weermodule bij Lagom te registreren:

public class WeatherServiceModule breidt uit AbstractModule implementeert ServiceGuiceSupport {@Override protected void configure () {bindServices (serviceBinding (WeatherService.class, WeatherServiceImpl.class)); }}

Registreer de weermodule ook in de lijst met ingeschakelde modules van Play:

play.modules.enabled + = com.baeldung.lagom.helloworld.weather.impl.WeatherServiceModule

8. Het project uitvoeren

Lagom staat toe het uitvoeren van een willekeurig aantal services samen met een enkele opdracht.

We kunnen ons project starten door op de onderstaande opdracht te drukken:

sbt lagom: runAll

Dit start het embedded Service Locator, ingebed Cassandra en start vervolgens microservices parallel. Dezelfde opdracht laadt ook onze individuele microservice opnieuw wanneer de code verandert, zodat we U hoeft ze niet handmatig opnieuw op te starten.

We kunnen gefocust zijn op onze logica en Lagom zorgt voor de compilatie en het herladen. Eenmaal succesvol gestart, zullen we de volgende output zien:

................ [info] Cassandra server draait op 127.0.0.1:4000 [info] Service locator draait op // localhost: 8000 [info] Service gateway draait op / / localhost: 9000 [info] Service weather-impl luisteren naar HTTP op 0: 0: 0: 0: 0: 0: 0: 0: 56231 en hoe de services samenwerken via [info] Service groet-impl luisteren naar HTTP op 0 : 0: 0: 0: 0: 0: 0: 0: 49356 [info] (Services gestart, druk op enter om te stoppen en terug te gaan naar de console ...)

Eenmaal succesvol gestart, kunnen we een curl-verzoek voor begroeting doen:

curl // localhost: 9000 / api / groet / Amit

We zullen de volgende uitvoer op de console zien:

Hallo Amit! Weerstatistieken van vandaag: Going to Rain, Take Umbrella

Als u hetzelfde curl-verzoek uitvoert voor een bestaande gebruiker, wordt het begroetingsbericht gewijzigd:

Hallo weer Amit! Weerstatistieken van vandaag: Going to Rain, Take Umbrella

9. Conclusie

In dit artikel hebben we besproken hoe u het Lagom-framework kunt gebruiken om twee microservices te maken die asynchroon samenwerken.

De volledige broncode en alle codefragmenten voor dit artikel zijn beschikbaar in het GitHub-project.