Događaji poslani s poslužitelja u proljeće

1. Pregled

U ovom uputstvu vidjet ćemo kako s Springom možemo implementirati API-je temeljene na poslužiteljskim događajima.

Jednostavno rečeno, Poslani događaji poslužitelja ili skraćeno SSE HTTP je standard koji omogućuje web aplikaciji da obrađuje jednosmjerni tok događaja i prima ažuriranja kad god poslužitelj emitira podatke.

Verzija Spring 4.2 već ga je podržavala, ali počevši od Spring 5, sada imamo idiomatičniji i prikladniji način za rukovanje.

2. SSE s proljećem 5 Webflux

Da biste to postigli, možemo se poslužiti implementacijama poput Fluks klasa koju pruža Reaktor knjižnica ili potencijalno ServerSentEvent entitet, koji nam daje kontrolu nad metapodacima događaja.

2.1. Strujanje događaja pomoću Fluks

Fluks je reaktivni prikaz toka događaja - njime se rukuje drugačije na temelju navedenog zahtjeva ili vrste medija za odgovor.

Da bismo stvorili SSE krajnju točku strujanja, morat ćemo slijediti specifikacije W3C i označiti njegov MIME tip kao tekst / tok-događaja:

@GetMapping (path = "/ stream-flux", stvara = MediaType.TEXT_EVENT_STREAM_VALUE) javni Flux streamFlux () {return Flux.interval (Duration.ofSeconds (1)) .map (slijed -> "Flux -" + LocalTime.now () .toString ()); }

The interval metoda stvara a Fluks koji emitira dugo vrijednosti postupno. Zatim te vrijednosti preslikavamo na željeni izlaz.

Pokrenimo našu aplikaciju i isprobajte je tada pregledavajući krajnju točku.

Vidjet ćemo kako će preglednik reagirati na događaje koje poslužitelj gura iz sekunde u sekundu. Za više informacija o Fluks i Jezgra reaktora, možemo pogledati ovaj post.

2.2. Koristeći ServerSentEvent Element

Sad ćemo zamotati naš izlaz Niz u a ServerSentSevent objekt i ispitati koristi od toga:

@GetMapping ("/ stream-sse") javni protok streamEvents () {return Flux.interval (Duration.ofSeconds (1)) .map (slijed -> ServerSentEvent. builder () .id (String.valueOf (slijed)) .event ("periodic-event") .data (" SSE - "+ LocalTime.now (). ToString ()) .build ()); }

Kao što možemo cijeniti, postoji nekoliko prednosti korištenja ServerSentEvent entitet:

  1. možemo se nositi s metapodacima događaja, koji bi nam trebali u stvarnom scenariju
  2. možemo zanemariti “tekst / tok-događaja”Izjava o vrsti medija

U ovom smo slučaju naveli iskaznica, an naziv događaja, i, što je najvažnije, stvarni podaci događaja.

Također, mogli smo dodati i komentari atribut i a pokušati ponovo vrijednost, koja će odrediti vrijeme ponovnog povezivanja koje će se koristiti prilikom pokušaja slanja događaja.

2.3. Konzumiranje događaja poslanih s poslužitelja s WebClientom

Sad konzumirajmo naš tok događaja s WebClient.:

javna praznina consumeServerSentEvent () {WebClient client = WebClient.create ("// localhost: 8080 / sse-server"); ParameterizedTypeReference type = novo ParameterizedTypeReference() {}; Fluks eventStream = client.get () .uri ("/ stream-sse") .retrieve () .bodyToFlux (vrsta); eventStream.subscribe (content -> logger.info ("Time: {} - event: name [{}], id [{}], content [{}]", LocalTime.now (), content.event (), content.id (), content.data ()), error -> logger.error ("Pogreška pri primanju SSE: {}", pogreška), () -> logger.info ("Dovršeno !!!")); }

The pretplatite se metoda omogućuje nam da naznačimo kako ćemo postupiti kada uspješno primimo događaj, kada se dogodi pogreška i kada je streaming završen.

U našem smo primjeru koristili dohvatiti metoda, koja je jednostavan i neposredan način dobivanja tijela odgovora.

Ova metoda automatski baca a WebClientResponseException ako primimo 4xx ili 5xx odgovor, osim ako se ne pozabavimo scenarijima dodavanjem onStatus izjava.

S druge strane, mogli smo koristiti razmjena metoda, koja omogućuje pristup ClientResponse a također ne signalizira grešku u slučaju neuspjelih odgovora.

Moramo uzeti u obzir da možemo zaobići ServerSentEvent omot ako nam ne trebaju metapodaci događaja.

3. SSE streaming u proljetnom MVC-u

Kao što smo rekli, SSE specifikacija podržana je od proljeća 4.2., Kada je SseEmitter uveden je razred.

Jednostavno rečeno, definirat ćemo ExecutorService, nit gdje SseEmitter će obaviti svoj posao gurajući podatke i vraćajući instancu emitera, održavajući vezu otvorenom na ovaj način:

@GetMapping ("/ stream-sse-mvc") javni SseEmitter streamSseMvc () {SseEmitter emitter = novi SseEmitter (); ExecutorService sseMvcExecutor = Izvršitelji.newSingleThreadExecutor (); sseMvcExecutor.execute (() -> {try {for (int i = 0; true; i ++) {SseEventBuilder event = SseEmitter.event () .data ("SSE MVC -" + LocalTime.now (). toString ()) .id (String.valueOf (i)) .name ("sse event - mvc"); emitter.send (event); Thread.sleep (1000);}} catch (Izuzetak ex) {emitter.completeWithError (ex); }}); povratni emiter; }

Uvijek pazite da odaberete pravo ExecutorService za vaš scenarij upotrebe.

Čitajući ovu zanimljivu lekciju možemo saznati više o SSE u Spring MVC-u i pogledati druge primjere.

4. Razumijevanje događaja poslanih s poslužitelja

Sad kad znamo kako implementirati SSE krajnje točke, pokušajmo malo dublje razumjeti neke temeljne koncepte.

SSE je specifikacija koju je većina preglednika usvojila kako bi omogućila strujanje događaja jednosmjerno u bilo kojem trenutku.

'Događaji' su samo tok tekstualnih podataka kodiranih UTF-8 koji slijede format definiran specifikacijom.

Ovaj se format sastoji od niza elemenata ključ / vrijednost (id, ponovni pokušaj, podaci i događaj, koji označava ime) odvojenih prelomima redaka.

Podržani su i komentari.

Specifikacija ni na koji način ne ograničava format korisnog tereta podataka; možemo se poslužiti jednostavnim Niz ili složenija JSON ili XML struktura.

Posljednja točka koju moramo uzeti u obzir je razlika između upotrebe SSE streaminga i WebSockets.

Dok WebSockets nude full-duplex (dvosmjernu) komunikaciju između poslužitelja i klijenta, dok SSE koristi jednosmjernu komunikaciju.

Također, WebSockets nije HTTP protokol i, suprotno SSE, ne nudi standarde za rukovanje pogreškama.

5. Zaključak

Da rezimiramo, u ovom smo članku naučili glavne koncepte SSE streaminga, što je nesumnjivo izvrstan resurs koji će nam omogućiti stvaranje sustava sljedeće generacije.

Sada smo u izvrsnoj poziciji da shvatimo što se događa ispod haube kada koristimo ovaj protokol.

Nadalje, teoriju smo nadopunili nekoliko jednostavnih primjera koji se mogu naći u našem Github spremištu.