Događaji poslani s poslužitelja (SSE) u JAX-RS

1. Pregled

Poslužitelji poslanih događaja (SSE) su HTTP specifikacija koja pruža način uspostavljanja dugotrajne i monokanalne veze od poslužitelja do klijenta.

Klijent pokreće SSE vezu pomoću vrste medija tekst / tok-događaja u Prihvatiti Zaglavlje.

Kasnije se automatski ažurira bez zahtjeva za poslužiteljem.

Više detalja o specifikacijama možemo provjeriti na službenim specifikacijama.

U ovom uputstvu predstavit ćemo novu implementaciju SSE JAX-RS 2.1.

Stoga ćemo pogledati kako možemo objavljivati ​​događaje s API-jem poslužitelja JAX-RS. Također ćemo istražiti kako ih možemo konzumirati ili putem JAX-RS klijentskog API-ja ili samo pomoću HTTP klijenta poput kovrča alat.

2. Razumijevanje SSE događaja

SSE događaj je blok teksta koji se sastoji od sljedećih polja:

  • Događaj: vrsta događaja. Poslužitelj može poslati mnogo poruka različitih vrsta, a klijent može slušati samo određenu vrstu ili može različito obraditi svaku vrstu događaja
  • Podaci: poruku koju je poslao poslužitelj. Možemo imati mnogo podatkovnih linija za isti događaj
  • Iskaznica: ID događaja koji se koristi za slanje datoteke ID zadnjeg događaja zaglavlje, nakon ponovnog pokušaja veze. Korisno je jer može spriječiti poslužitelj da šalje već poslane događaje
  • Pokušaj ponovo: vrijeme u milisekundama da klijent uspostavi novu vezu kada se izgubi struja. Posljednji primljeni ID automatski će se poslati putem ID zadnjeg događaja Zaglavlje
  • :‘: Ovo je komentar i klijent ga ignorira

Također, dva uzastopna događaja odvojena su dvostrukim novim redom '\ n \ n‘.

Uz to, podaci u istom događaju mogu se zapisati u više redaka što se može vidjeti u sljedećem primjeru:

događaj: ID dionice: 1: Pokušaj promjene cijene: 4000 podataka: {"dateTime": "2018-07-14T18: 06: 00.285", "id": 1, podaci: "name": "GOOG", "price" : 75.7119} događaj: ID dionice: 2: Pokušaj promjene cijene: 4000 podataka: {"dateTime": "2018-07-14T18: 06: 00.285", "id": 2, "name": "IBM", "price ": 83,4611}

U JAX RS, SSE događaj apstrahira SseEvent sučelje, ili točnije, s dva podsučelja OutboundSseEvent i InboundSseEvent.

Dok OutboundSseEvent koristi se na API-ju poslužitelja i dizajnira poslani događaj, InboundSseEvent koristi se klijentskim API-jem i apstrahira primljeni događaj.

3. Objavljivanje SSE događaja

Sad kad smo razgovarali o tome što je SSE događaj, pogledajmo kako ga možemo izgraditi i poslati HTTP klijentu.

3.1. Postavljanje projekta

Već imamo vodič o postavljanju projekta Maven sa sjedištem u JAX-u RS. Slobodno pogledajte tamo kako biste postavili ovisnosti i započeli s radom s JAX RS.

3.2. SSE metoda resursa

Metoda SSE resursa je metoda JAX RS koja:

  • Može proizvesti tekst / tok-događaja vrsta medija
  • Ima injekciju SseEventSink parametar, gdje se šalju događaji
  • Može također dobiti injekciju Sse parametar koji se koristi kao ulazna točka za stvaranje graditelja događaja
@GET @Path ("prices") @Produces ("text / event-stream") javna praznina getStockPrice (@Context SseEventSink sseEventSink, @Context Sse sse) {// ...}

Kao posljedicu, klijent bi trebao poslati prvi HTTP zahtjev, sa sljedećim HTTP zaglavljem:

Prihvati: tekst / stream događaja 

3.3. Instanca SSE

Instanca SSE kontekstni je grah koji će JAX RS Runtime učiniti dostupnim za ubrizgavanje.

Mogli bismo ga koristiti kao tvornicu za stvaranje:

  • OutboundSseEvent.Builder - omogućuje nam tada stvaranje događaja
  • SseBroadcaster - omogućuje nam emitiranje događaja za više pretplatnika

Pogledajmo kako to funkcionira:

@Context javna praznina setSse (Sse sse) {this.sse = sse; this.eventBuilder = sse.newEventBuilder (); this.sseBroadcaster = sse.newBroadcaster (); }

Sada ćemo se usredotočiti na graditelj događaja. OutboundSseEvent.Builder odgovoran je za stvaranje OutboundSseEvent:

OutboundSseEvent sseEvent = this.eventBuilder .name ("stock") .id (String.valueOf (lastEventId)) .mediaType (MediaType.APPLICATION_JSON_TYPE) .data (Stock.class, stock) .reconnectDelay (4000) .comment ("promjena cijene" ") .build ();

Kao što vidimo, graditelj ima metode za postavljanje vrijednosti za sva gore navedena polja događaja. Uz to, vrsta medija() metoda koristi se za serializaciju podatkovnog polja Java objekt u prikladan format teksta.

Prema zadanim postavkama vrsta medija podatkovnog polja je tekst / običan. Stoga ga ne treba izričito navesti kada se radi o Niz vrsta podataka.

Inače, ako želimo obraditi prilagođeni objekt, moramo navesti vrstu medija ili pružiti prilagođeni MessageBodyWriter.JAX RS Runtime pruža MessageBodyWriters za najpoznatije vrste medija.

Instanca Sse također ima dva prečaca graditelja za stvaranje događaja samo s podatkovnim poljem ili tipom i poljima podataka:

OutboundSseEvent sseEvent = sse.newEvent ("cool događaj"); OutboundSseEvent sseEvent = sse.newEvent ("upisani događaj", "podatkovni događaj");

3.4. Slanje jednostavnog događaja

Sad kad znamo kako graditi događaje i razumijemo kako funkcionira SSE resurs. Pošaljite jednostavan događaj.

The SseEventSink sučelje apstrahira jednu HTTP vezu. JAX-RS Runtime može ga učiniti dostupnim samo ubrizgavanjem u SSE metodu resursa.

Slanje događaja tada je jednostavno poput pozivanja SseEventSink.poslati().

U sljedećem će primjeru poslati hrpu ažuriranja dionica i na kraju zatvoriti tok događaja:

@GET @Path ("prices") @Produces ("text / event-stream") javna praznina getStockPrice (@Context SseEventSink sseEventSink /*..*/) {int lastEventId = // ..; dok je (pokrenuto) {Stock stock = stockService.getNextTransaction (lastEventId); if (zaliha! = null) {OutboundSseEvent sseEvent = this.eventBuilder .name ("stock") .id (String.valueOf (lastEventId)) .mediaType (MediaType.APPLICATION_JSON_TYPE) .data (Stock.class, stock) .reconnectDelay ( 3000) .comment ("promjena cijene") .build (); sseEventSink.send (sseEvent); lastEventId ++; } // ..} sseEventSink.close (); }

Nakon slanja svih događaja, poslužitelj zatvara vezu bilo izričitim pozivanjem na Zatvoriti() metodom ili, po mogućnosti, korištenjem pokušajte s resursom, kao SseEventSink proširuje Automatsko zatvaranje sučelje:

probajte (SseEventSink sink = sseEventSink) {OutboundSseEvent sseEvent = // .. sink.send (sseEvent); }

U našem primjeru aplikacije možemo vidjeti kako se pokreće ako posjetimo:

//localhost:9080/sse-jaxrs-server/sse.html

3.5. Emitiranje događaja

Emitiranje je postupak kojim se događaji istovremeno šalju više klijentima. To postiže SseBroadcaster API, a radi se u tri jednostavna koraka:

Prvo, kreiramo SseBroadcaster objekt iz ubrizganog Sse konteksta kako je prethodno prikazano:

SseBroadcaster sseBroadcaster = sse.newBroadcaster ();

Tada bi se klijenti trebali pretplatiti kako bi mogli primati Sse Events. To se obično radi u SSE metodi resursa gdje a SseEventSink ubrizgava se primjer konteksta:

@GET @Path ("pretplatite se") @Produces (MediaType.SERVER_SENT_EVENTS) javno void preslušavanje (@Context SseEventSink sseEventSink) {this.sseBroadcaster.register (sseEventSink); }

I konačno, objavljivanje događaja možemo pokrenuti pozivom na emitiranje () metoda:

@GET @Path ("objaviti") javno void emitiranje () {OutboundSseEvent sseEvent = // ...; this.sseBroadcaster.broadcast (sseEvent); }

Ovo će poslati isti događaj svim prijavljenim SseEventSink.

Da bismo prikazali emitiranje, možemo pristupiti ovom URL-u:

//localhost:9080/sse-jaxrs-server/sse-broadcast.html

A zatim možemo pokrenuti emitiranje pozivajući se na metodu resursa broadcast ():

curl -X GET // localhost: 9080 / sse-jaxrs-server / sse / stock / objaviti

4. Konzumiranje SSE događaja

Da bismo konzumirali SSE događaj koji je poslao poslužitelj, možemo koristiti bilo koji HTTP klijent, ali za ovaj ćemo vodič koristiti JAX RS klijentski API.

4.1. API JAX RS klijenta za SSE

Da bismo započeli s klijentskim API-jem za SSE, moramo osigurati ovisnosti za implementaciju JAX RS klijenta.

Ovdje ćemo koristiti implementaciju Apache CXF klijenta:

 org.apache.cxf cxf-rt-rs-client $ {cxf-version} org.apache.cxf cxf-rt-rs-sse $ {cxf-version} 

The SseEventSource je srce ovog API-ja, a napravljen je od WebTarget.

Započinjemo sa preslušavanjem dolaznih događaja čije apstrahira InboundSseEvent sučelje:

Klijent klijent = ClientBuilder.newClient (); WebTarget target = client.target (url); pokušajte (SseEventSource izvor = SseEventSource.target (target) .build ()) {source.register ((inboundSseEvent) -> System.out.println (inboundSseEvent)); source.open (); }

Nakon uspostavljanja veze, za svakog primljenog zatražit će se registrirani potrošač događaja InboundSseEvent.

Tada možemo koristiti readData () metoda za čitanje izvornih podataka Niz:

Podaci niza = inboundSseEvent.readData ();

Ili možemo koristiti preopterećenu verziju kako bismo dobili Deserijalizirani Java objekt koristeći prikladnu vrstu medija:

Zaliha dionica = inboundSseEvent.readData (Stock.class, MediaType.Application_Json);

Ovdje smo upravo pružili jednostavnog potrošača događaja koji ispisuje dolazni događaj u konzoli.

5. Zaključak

U ovom smo se vodiču usredotočili na upotrebu poslužiteljski poslanih događaja u JAX RS 2.1. Pružili smo primjer koji prikazuje kako slati događaje jednom klijentu, kao i kako emitirati događaje višestrukim klijentima.

Konačno, konzumirali smo ove događaje koristeći API klijenta JAX-RS.

Kao i obično, kod ovog vodiča možete pronaći na Githubu.