Programmering

Mastering Spring framework 5, Part 2: Spring WebFlux

Spring WebFlux introduserer reaktiv nettutvikling til Spring økosystemet. Denne artikkelen kommer i gang med reaktive systemer og reaktiv programmering med Spring. Først vil du finne ut hvorfor reaktive systemer er viktige og hvordan de implementeres i Spring Framework 5, så får du en praktisk introduksjon til å bygge reaktive tjenester ved hjelp av Spring WebFlux. Vi bygger vår første reaktive applikasjon ved hjelp av merknader. Jeg vil også vise deg hvordan du bygger en lignende applikasjon ved hjelp av Spring's nyere funksjonelle funksjoner.

Vårveiledninger om JavaWorld

Hvis du er ny på Spring-rammeverket, anbefaler jeg at du starter med en av de tidligere opplæringene i denne serien:

  • Hva er vår? Komponentbasert utvikling for Java
  • Mastering Spring framework 5: Spring MVC

Reaktive systemer og Spring WebFlux

Begrepet reaktiv er for tiden populær blant utviklere og IT-ledere, men jeg har lagt merke til litt usikkerhet om hva det egentlig betyr. For å bli tydeligere på hva reaktive systemer er, er det nyttig å forstå det grunnleggende problemet de er designet for å løse. I denne delen vil vi snakke om reaktive systemer generelt, og jeg vil introdusere Reactive Streams API for Java-applikasjoner.

Skalerbarhet i vår-MVC

Spring MVC har tjent sin plass blant de beste valgene for å bygge Java-webapplikasjoner og webtjenester. Som vi oppdaget i Mastering Spring framework 5, Part 1, integrerer Spring MVC sømløst annoteringer i den robuste arkitekturen til en Spring-basert applikasjon. Dette gjør at utviklere kjent med Spring raskt kan bygge tilfredsstillende, svært funksjonelle webapplikasjoner. Skalerbarhet er imidlertid en utfordring for Spring MVC-applikasjoner. Det er problemet Spring WebFlux søker å løse.

Blokkering mot ikke-blokkerende nettrammer

I tradisjonelle webapplikasjoner, når en webserver mottar en forespørsel fra en klient, godtar den forespørselen og plasserer den i en kjøringskø. En tråd i kjøringens trådpool mottar deretter forespørselen, leser inngangsparametrene og genererer et svar. Underveis, hvis kjøringstråden trenger å ringe til en blokkeringsressurs - for eksempel en database, et filsystem eller en annen webtjeneste - kjører den tråden blokkeringsforespørselen og venter på svar. I dette paradigmet blokkeres tråden effektivt til den eksterne ressursen reagerer, noe som forårsaker ytelsesproblemer og begrenser skalerbarhet. For å bekjempe disse problemene oppretter utviklere generøse trådbassenger, slik at mens en tråd er blokkert kan en annen tråd fortsette å behandle forespørsler. Figur 1 viser utførelsesflyten for en tradisjonell, blokkerende webapplikasjon.

Steven Haines

Ikke-blokkerende nettrammer som NodeJS og Play tar en annen tilnærming. I stedet for å utføre en blokkeringsforespørsel og vente på at den skal fullføres, bruker de ikke-blokkerende I / U. I dette paradigmet utfører et program en forespørsel, gir kode som skal utføres når et svar returneres, og gir deretter tråden tilbake til serveren. Når en ekstern ressurs returnerer et svar, blir den oppgitte koden utført. Internt fungerer ikke-blokkerende rammer ved hjelp av en hendelsessløyfe. Innen sløyfen gir applikasjonskoden enten en tilbakeringing eller en fremtid som inneholder koden som skal utføres når den asynkrone sløyfen er fullført.

Av natur er ikke-blokkerende rammer hendelsesdrevet. Dette krever et annet programmeringsparadigme og en ny tilnærming til resonnement om hvordan koden din blir utført. Når du har pakket hodet rundt det, kan reaktiv programmering føre til veldig skalerbare applikasjoner.

Tilbakekallinger, løfter og fremtid

I de tidlige dager håndterte JavaScript all asynkron funksjonalitet via tilbakeringinger. I dette scenariet, når en hendelse inntreffer (for eksempel når et svar fra en tjenesteanrop blir tilgjengelig), blir tilbakeringingen utført. Mens tilbakeringing fremdeles er utbredt, har JavaScript asynkrone funksjonalitet nylig flyttet til lover. Med løfter returnerer en funksjonsanrop umiddelbart, og returnerer et løfte om å levere resultatene på et fremtidig tidspunkt. I stedet for løfter implementerer Java et lignende paradigme ved hjelp futures. I denne bruken returnerer en metode en fremtid som vil ha en verdi en gang i fremtiden.

Reaktiv programmering

Du har kanskje hørt ordet reaktiv programmering relatert til rammer og verktøy for webutvikling, men hva betyr det egentlig? Begrepet som vi har lært å kjenne, stammer fra Reactive Manifesto, som definerer reaktive systemer som fire kjernetrekk:

  1. Reaktive systemer er mottakelig, noe som betyr at de svarer i tide, under alle mulige omstendigheter. De fokuserer på å gi raske og konsistente responstider, etablere pålitelige øvre grenser slik at de leverer en jevn kvalitet på tjenesten.
  2. Reaktive systemer er fleksibel, noe som betyr at de fortsatt er lydhøre overfor feil. Motstandsdyktighet oppnås ved teknikker for replikering, inneslutning, isolasjon og delegering. Ved å isolere applikasjonskomponenter fra hverandre kan du inneholde feil og beskytte systemet som helhet.
  3. Reaktive systemer er elastisk, noe som betyr at de er lydhøre under varierende arbeidsbelastning. Dette oppnås ved å skalere applikasjonskomponenter elastisk for å dekke dagens behov.
  4. Reaktive systemer er meldingsdrevet, noe som betyr at de er avhengige av asynkron melding som går mellom komponentene. Dette lar deg lage løs kobling, isolasjon og plasseringsgjennomsiktighet.

Figur 2 viser hvordan disse egenskapene flyter sammen i et reaktivt system.

Steven Haines

Kjennetegn ved et reaktivt system

Reaktive systemer bygges ved å lage isolerte komponenter som kommuniserer med hverandre asynkront og kan skaleres raskt for å møte gjeldende belastning. Komponenter mislykkes fortsatt i reaktive systemer, men det er definerte handlinger som skal utføres som et resultat av feilen, noe som holder systemet som helhet funksjonelt og responsivt.

De Reaktivt manifest er abstrakt, men reaktive applikasjoner er typisk preget av følgende komponenter eller teknikker:

  • Datastrømmer: A strøm er en sekvens av hendelser som er bestilt i tide, for eksempel brukerinteraksjoner, REST-tjenesteanrop, JMS-meldinger og resultater fra en database.
  • Asynkron: Datastrømhendelser fanges asynkront, og koden din definerer hva du skal gjøre når en hendelse sendes ut, når en feil oppstår og når strømmen av hendelser er fullført.
  • Ikke-blokkerende: Når du behandler hendelser, skal ikke koden din blokkere og utføre synkrone anrop. i stedet bør den ringe asynkrone samtaler og svare når resultatene av disse samtalene returneres.
  • Mottrykk: Komponenter styrer antall hendelser og hvor ofte de sendes ut. I reaktive termer blir komponenten din referert til som abonnent og hendelser sendes ut av en forlegger. Dette er viktig fordi abonnenten har kontroll over hvor mye data den mottar og dermed ikke vil belaste seg selv.
  • Feilmeldinger: I stedet for at komponenter kaster unntak, sendes feil som meldinger til en behandlerfunksjon. Mens kaste unntak bryter strømmen, definerer det ikke en funksjon for å håndtere feil som de oppstår.

API-et for reaktive strømmer

Den nye API-en for Reactive Streams ble opprettet av ingeniører fra blant annet Netflix, Pivotal, Lightbend, RedHat, Twitter og Oracle. Publisert i 2015, er Reactive Streams API nå en del av Java 9. Den definerer fire grensesnitt:

  • Forlegger: Avgir en sekvens av hendelser til abonnenter.
  • Abonnent: Mottar og behandler hendelser som sendes ut av en utgiver.
  • Abonnement: Definerer en en-til-en-relasjon mellom en forlegger og en abonnent.
  • Prosessor: Representerer et behandlingsstadium bestående av både en abonnent og en utgiver og overholder begge kontraktene.

Figur 3 viser forholdet mellom en utgiver, abonnent og abonnement.

Steven Haines

I hovedsak oppretter en abonnent et abonnement på en utgiver, og når utgiveren har tilgjengelige data, sender den en hendelse til abonnenten med en strøm av elementer. Merk at abonnenten klarer mottrykket i abonnementet på utgiveren.

Nå som du vet litt om reaktive systemer og Reactive Streams API, la oss rette oppmerksomheten mot verktøyene Spring bruker for å implementere reaktive systemer: Spring WebFlux og Reactor-biblioteket.

Prosjektreaktor

Project Reactor er et tredjepartsrammeverk basert på Java's Reactive Streams Specification, som brukes til å bygge ikke-blokkerende webapplikasjoner. Project Reactor tilbyr to utgivere som er mye brukt i Spring WebFlux:

  • Mono: Returnerer 0 eller 1 element.
  • Flux: Returnerer 0 eller flere elementer. En flux kan være uendelig, noe som betyr at den kan fortsette å sende ut elementer for alltid, eller den kan returnere en sekvens av elementer og deretter sende et fullstendig varsel når den har returnert alle elementene.

Monoer og strømmer er konseptuelt lik futures, men kraftigere. Når du påkaller en funksjon som returnerer en mono eller en fluks, vil den returnere umiddelbart. Resultatene av funksjonsanropet vil bli levert til deg via mono eller flux når de blir tilgjengelige.

I Spring WebFlux vil du ringe reaktive biblioteker som returnerer monoer og strømmer, og kontrollerne dine vil returnere monoer og strømninger. Fordi disse kommer tilbake umiddelbart, vil kontrollerne dine effektivt gi opp trådene sine og la Reactor håndtere svar asynkront. Det er viktig å merke seg at bare ved å bruke reaktive biblioteker kan WebFlux-tjenestene forbli reaktive. Hvis du bruker ikke-reaktive biblioteker, for eksempel JDBC-samtaler, vil koden din blokkere og vente på at samtalene skal fullføres før du returnerer.

Reaktiv programmering med MongoDB

For øyeblikket er det ikke mange reaktive databasebiblioteker, så du lurer kanskje på om det er praktisk å skrive reaktive tjenester. Den gode nyheten er at MongoDB har reaktiv støtte, og det er et par tredjeparts reaktive databasedrivere for MySQL og Postgres. For alle andre brukssaker gir WebFlux en mekanisme for å utføre JDBC-samtaler på en reaktiv måte, om enn ved hjelp av en sekundær trådgruppe som gjør det mulig å blokkere JDBC-samtaler.

Kom i gang med Spring WebFlux

For vårt første hvordan-eksempel vil vi lage en enkel boktjeneste som fortsetter bøker til og fra MongoDB på en reaktiv måte.

Start med å navigere til våren Initializr hjemmeside, hvor du velger en Maven prosjekt med Java og velg den nyeste versjonen av Spring Boot (2.0.3 i skrivende stund). Gi prosjektet et gruppenavn, for eksempel "com.javaworld.webflux", og et gjenstandsnavn, for eksempel "bookservice". Utvid Bytt til fullversjonen lenke for å vise hele listen over avhengigheter. Velg følgende avhengigheter for applikasjonen:

  • Internett -> Reaktivt nett: Denne avhengigheten inkluderer Spring WebFlux.
  • NoSQL -> Reaktiv MongoDB: Denne avhengigheten inkluderer de reaktive driverne for MongoDB.
  • NoSQL -> Embedded MongoDB: Denne avhengigheten lar oss kjøre en innebygd versjon av MongoDB, så det er ikke nødvendig å installere en separat forekomst. Vanligvis brukes dette til testing, men vi vil inkludere det i utgivelseskoden for å unngå å installere MongoDB.
  • Kjerne -> Lombok: Å bruke Lombok er valgfritt, ettersom du ikke trenger det for å bygge et Spring WebFlux-program. Fordelen med å bruke Project Lombok er at den lar deg legge til kommentarer til klasser som automatisk genererer getters og settere, konstruktører, hashCode (), er lik(), og mer.

Når du er ferdig, bør du se noe som ligner på figur 4.

Steven Haines

Pressing Generer prosjekt vil utløse nedlastingen av en zip-fil som inneholder prosjektets kildekode. Pakk ut den nedlastede filen og åpne den i din favoritt IDE. Hvis du bruker IntelliJ, velger du Fil og så Åpen, og naviger til katalogen der den nedlastede zip-filen er dekomprimert.

Du vil finne at Spring Initializr har generert to viktige filer:

  1. En Maven pom.xml filen, som inkluderer alle nødvendige avhengigheter for applikasjonen.
  2. BookserviceApplication.java, som er startklassen Spring Boot for applikasjonen.

Oppføring 1 viser innholdet i den genererte pom.xml-filen.

$config[zx-auto] not found$config[zx-overlay] not found