Server-sent Events (SSE) I JAX-RS

1. Oversikt

Server-Sent Events (SSE) er en HTTP-basert spesifikasjon som gir en måte å etablere en langvarig og monokanalforbindelse fra serveren til klienten.

Klienten starter SSE-tilkoblingen ved hjelp av medietypen tekst / event-stream i Aksepterer Overskrift.

Senere blir den oppdatert automatisk uten å be om serveren.

Vi kan sjekke mer informasjon om spesifikasjonen på den offisielle spesifikasjonen.

I denne veiledningen introduserer vi den nye JAX-RS 2.1-implementeringen av SSE.

Derfor vil vi se på hvordan vi kan publisere hendelser med JAX-RS Server API. Vi vil også undersøke hvordan vi kan konsumere dem enten av JAX-RS Client API eller bare av en HTTP-klient som krølle verktøy.

2. Forstå SSE-hendelser

En SSE-hendelse er en tekstblokk som består av følgende felt:

  • Begivenhet: arrangementets type. Serveren kan sende mange meldinger av forskjellige typer, og klienten kan bare lytte etter en bestemt type eller kan behandle forskjellige hver hendelsestype
  • Data: meldingen sendt av serveren. Vi kan ha mange datalinjer for den samme hendelsen
  • Id: id for arrangementet, brukes til å sende Siste hendelse-ID topptekst, etter en tilkobling på nytt. Det er nyttig fordi det kan forhindre serveren i å sende allerede sendte hendelser
  • Prøv på nytt: tiden, i millisekunder, for klienten å opprette en ny forbindelse når strømmen går tapt. Den sist mottatte ID vil automatisk bli sendt gjennom Siste hendelse-ID Overskrift
  • :‘: Dette er en kommentar og ignoreres av klienten

Også to påfølgende hendelser er atskilt med en dobbel ny linje ‘\ n \ n‘.

I tillegg kan dataene i samme hendelse skrives i mange linjer som kan sees i følgende eksempel:

hendelse: lager-id: 1: prisendring på nytt: 4000 data: {"dateTime": "2018-07-14T18: 06: 00.285", "id": 1, data: "name": "GOOG", "price" : 75.7119} hendelse: lager-id: 2: prisendring på nytt: 4000 data: {"dateTime": "2018-07-14T18: 06: 00.285", "id": 2, "name": "IBM", "price ": 83.4611}

I JAX RS, blir en SSE-begivenhet abstrakt av SseEvent grensesnitt, eller mer presist, av de to delgrensesnittene OutboundSseEvent og InboundSseEvent.

Mens OutboundSseEvent brukes på Server API og designer en sendt hendelse, the InboundSseEvent brukes av Client API og trekker ut en mottatt hendelse.

3. Publisering av SSE-hendelser

Nå som vi diskuterte hva en SSE-hendelse er, la oss se hvordan vi kan bygge og sende den til en HTTP-klient.

3.1. Prosjektoppsett

Vi har allerede en veiledning om å sette opp et JAX RS-basert Maven-prosjekt. Ta gjerne en titt der for å se hvordan du setter avhengigheter og kommer i gang med JAX RS.

3.2. SSE-ressursmetode

En SSE-ressursmetode er en JAX RS-metode som:

  • Kan produsere en tekst / event-stream Media type
  • Har en injisert SseEventSink parameter, der hendelser sendes
  • Kan også få en injisert Sse parameter som brukes som inngangspunkt for å opprette en hendelsesbygger
@GET @Path ("prices") @Produces ("text / event-stream") public void getStockPrices (@Context SseEventSink sseEventSink, @Context Sse sse) {// ...}

Som en konsekvens skal klienten sende den første HTTP-forespørselen med følgende HTTP-overskrift:

Godta: tekst / hendelsesstrøm 

3.3. SSE-forekomsten

En SSE-forekomst er en kontekstbønne som JAX RS Runtime vil gjøre tilgjengelig for injeksjon.

Vi kan bruke den som en fabrikk for å lage:

  • OutboundSseEvent.Builder - tillater oss å lage arrangementer da
  • SseBroadcaster - lar oss kringkaste hendelser til flere abonnenter

La oss se hvordan det fungerer:

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

La oss nå fokusere på arrangementbyggeren. OutboundSseEvent.Builder er ansvarlig for å skape OutboundSseEvent:

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

Som vi kan se, byggherren har metoder for å sette verdier for alle hendelsesfeltene vist ovenfor. I tillegg har Media type() metoden brukes til å serialisere datafeltet Java-objekt til et passende tekstformat.

Som standard er medietypen i datafeltet tekst / vanlig. Derfor trenger det ikke å spesifiseres spesifikt når du arbeider med String data-type.

Hvis ikke, hvis vi vil håndtere et tilpasset objekt, må vi spesifisere medietypen eller oppgi en tilpasset MessageBodyWriter.JAX RS Runtime gir MessageBodyWriters for de mest kjente mediatypene.

Sse-forekomsten har også to snarveier for byggere for å opprette en hendelse med bare datafeltet, eller type- og datafeltene:

OutboundSseEvent sseEvent = sse.newEvent ("kul begivenhet"); OutboundSseEvent sseEvent = sse.newEvent ("typed event", "data Event");

3.4. Sender enkel begivenhet

Nå som vi vet hvordan vi skal bygge hendelser, og vi forstår hvordan en SSE-ressurs fungerer. La oss sende en enkel begivenhet.

De SseEventSink grensesnittet trekker ut en enkelt HTTP-tilkobling. JAX-RS Runtime kan gjøre den bare tilgjengelig gjennom injeksjon i SSE-ressursmetoden.

Å sende en hendelse er da så enkelt som å påkalle SseEventSink.sende().

I det neste eksemplet vil du sende en haug med lageroppdateringer og til slutt vil lukke hendelsesstrømmen:

@GET @Path ("priser") @Produces ("text / event-stream") offentlig ugyldighet getStockPrices (@Context SseEventSink sseEventSink /*..*/) {int lastEventId = // ..; mens (kjører) {Stock stock = stockService.getNextTransaction (lastEventId); if (stock! = null) {OutboundSseEvent sseEvent = this.eventBuilder .name ("stock") .id (String.valueOf (lastEventId)) .mediaType (MediaType.APPLICATION_JSON_TYPE) .data (Stock.class, stock) .reconnectDelay ( 3000) .kommentar ("prisendring") .bygg (); sseEventSink.send (sseEvent); lastEventId ++; } // ..} sseEventSink.close (); }

Etter å ha sendt alle hendelser, lukker serveren tilkoblingen enten ved eksplisitt å påkalle Lukk() metoden eller fortrinnsvis ved å bruke prøv-med-ressurs, som SseEventSink utvider Kan lukkes automatisk grensesnitt:

prøv (SseEventSink vask = sseEventSink) {OutboundSseEvent sseEvent = // .. sink.send (sseEvent); }

I eksempelappen vår kan vi se at dette kjører hvis vi besøker:

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

3.5. Kringkastingshendelser

Kringkasting er prosessen der hendelser sendes til flere klienter samtidig. Dette oppnås av SseBroadcaster API, og det gjøres i tre enkle trinn:

Først lager vi SseBroadcaster objekt fra en injisert Sse-kontekst som vist tidligere:

SseBroadcaster sseBroadcaster = sse.newBroadcaster ();

Deretter bør klienter abonnere for å kunne motta Sse-hendelser. Dette gjøres vanligvis i en SSE-ressursmetode der en SseEventSink kontekstforekomst injiseres:

@GET @Path ("subscribe") @Produces (MediaType.SERVER_SENT_EVENTS) public void listen (@Context SseEventSink sseEventSink) {this.sseBroadcaster.register (sseEventSink); }

Og endelig, vi kan utløse hendelsespublisering ved å påkalle kringkaste() metode:

@GET @Path ("publiser") offentlig ugyldig kringkasting () {OutboundSseEvent sseEvent = // ...; this.sseBroadcaster.broadcast (sseEvent); }

Dette vil sende den samme hendelsen til hver registrerte SseEventSink.

For å vise kringkastingen kan vi få tilgang til denne URL:

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

Og så kan vi utløse kringkastingen ved å påkalle kringkastingsmetoden ():

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

4. Forbruker SSE-hendelser

For å konsumere en SSE-hendelse sendt av serveren, kan vi bruke hvilken som helst HTTP-klient, men for denne opplæringen bruker vi JAX RS-klient-API.

4.1. JAX RS Client API for SSE

For å komme i gang med klient-API for SSE, må vi tilby avhengigheter for JAX RS-klientimplementering.

Her bruker vi implementering av Apache CXF-klient:

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

De SseEventSource er hjertet i dette API-et, og det er konstruert fra The WebTarget.

Vi begynner med å lytte etter innkommende hendelser som er abstrakt av InboundSseEvent grensesnitt:

Klientklient = ClientBuilder.newClient (); WebTarget target = client.target (url); prøv (SseEventSource source = SseEventSource.target (target) .build ()) {source.register ((inboundSseEvent) -> System.out.println (inboundSseEvent)); source.open (); }

Når forbindelsen er opprettet, blir den registrerte hendelsesforbrukeren påkalt for hver mottatt InboundSseEvent.

Vi kan da bruke readData () metode for å lese originaldataene Streng:

Strengdata = inboundSseEvent.readData ();

Eller vi kan bruke den overbelastede versjonen for å få deserialisert Java-objekt ved hjelp av passende mediatype:

Lagerbeholdning = inboundSseEvent.readData (lager.klasse, MediaType.Application_Json);

Her ga vi nettopp en enkel hendelsesforbruker som skriver ut den innkommende hendelsen i konsollen.

5. Konklusjon

I denne opplæringen fokuserte vi på hvordan du bruker Server-Sent Events i JAX RS 2.1. Vi ga et eksempel som viser hvordan du sender hendelser til en enkelt klient, samt hvordan du sender hendelser til flere klienter.

Til slutt konsumerte vi disse hendelsene ved hjelp av JAX-RS-klient-API.

Som vanlig kan koden i denne opplæringen finnes på Github.


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