Acara Dihantar Pelayan (SSE) Di JAX-RS

1. Gambaran keseluruhan

Server-Sent Events (SSE) adalah spesifikasi berdasarkan HTTP yang menyediakan cara untuk mewujudkan sambungan saluran lama dan mono dari pelayan ke klien.

Pelanggan memulakan sambungan SSE dengan menggunakan teks jenis media / aliran peristiwa di tajuk Terima .

Kemudian, ia dikemas kini secara automatik tanpa meminta pelayan.

Kami dapat memeriksa lebih banyak perincian mengenai spesifikasi pada spesifikasi rasmi.

Dalam tutorial ini, kami akan memperkenalkan pelaksanaan SSA JAX-RS 2.1 baru.

Oleh itu, kita akan melihat bagaimana kita dapat menerbitkan acara dengan JAX-RS Server API. Juga, kita akan meneroka bagaimana kita boleh menggunakannya baik dengan JAX-RS Client API atau hanya oleh klien HTTP seperti alat curl .

2. Memahami Acara SSE

Acara SSE adalah sekumpulan teks yang terdiri daripada bidang berikut:

  • Acara: jenis acara. Pelayan boleh menghantar banyak mesej dari pelbagai jenis dan klien hanya boleh mendengar jenis tertentu atau dapat memproses secara berbeza setiap jenis peristiwa
  • Data: mesej yang dihantar oleh pelayan. Kita boleh mempunyai banyak baris data untuk acara yang sama
  • Id: id acara, digunakan untuk menghantar header Last-Event-ID , setelah sambungan dicuba semula. Ia berguna kerana dapat mengelakkan pelayan menghantar acara yang sudah dihantar
  • Cuba semula: masa, dalam milisaat, untuk pelanggan membuat sambungan baru apabila arus hilang. Id yang terakhir diterima akan dihantar secara automatik melalui header Last-Event-ID
  • ' : ': ini adalah komen dan tidak diendahkan oleh pelanggan

Juga, dua acara berturut-turut dipisahkan oleh baris baru berganda ' \ n \ n '.

Selain itu, data dalam acara yang sama dapat ditulis dalam banyak baris seperti yang dapat dilihat dalam contoh berikut:

event: stock id: 1 : price change retry: 4000 data: {"dateTime":"2018-07-14T18:06:00.285","id":1, data: "name":"GOOG","price":75.7119} event: stock id: 2 : price change retry: 4000 data: {"dateTime":"2018-07-14T18:06:00.285","id":2,"name":"IBM","price":83.4611}

Di JAX RS , peristiwa SSE disarikan oleh antara muka SseEvent , atau lebih tepatnya, oleh dua subinterfaces OutboundSseEvent dan InboundSseEvent.

Walaupun OutboundSseEvent digunakan pada API Server dan merancang acara yang dihantar, InboundSseEvent digunakan oleh Client API dan mengabstrak peristiwa yang diterima .

3. Penerbitan Acara SSE

Sekarang setelah kita membincangkan apa itu peristiwa SSE, mari kita lihat bagaimana kita dapat membina dan menghantarnya ke klien HTTP.

3.1. Penyediaan Projek

Kami sudah mempunyai tutorial untuk menyiapkan projek Maven berasaskan JAX RS. Jangan ragu untuk melihat di sana untuk melihat cara mengatur pergantungan dan memulakan dengan JAX RS.

3.2. Kaedah Sumber SSE

Kaedah Sumber SSE adalah kaedah JAX RS yang:

  • Boleh menghasilkan jenis media teks / peristiwa-aliran
  • Mempunyai parameter SseEventSink yang disuntikkan , tempat peristiwa dihantar
  • Mungkin juga memiliki parameter Sse yang disuntikkan yang digunakan sebagai titik masuk untuk membuat pembangun acara
@GET @Path("prices") @Produces("text/event-stream") public void getStockPrices(@Context SseEventSink sseEventSink, @Context Sse sse) { //... }

Akibatnya, klien harus membuat permintaan HTTP pertama, dengan tajuk HTTP berikut:

Accept: text/event-stream 

3.3. Contoh SSE

Contoh SSE adalah kacang konteks yang akan disediakan JAX RS Runtime untuk suntikan.

Kita boleh menggunakannya sebagai kilang untuk membuat:

  • OutboundSseEvent.Builder - membolehkan kita membuat acara kemudian
  • SseBroadcaster - membolehkan kita menyiarkan acara kepada beberapa pelanggan

Mari lihat bagaimana ia berfungsi:

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

Sekarang, mari fokus pada pembangun acara. OutboundSseEvent.Builder bertanggungjawab untuk membuat OutboundSseEvent :

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

Seperti yang kita lihat, pembangun mempunyai kaedah untuk menetapkan nilai untuk semua bidang acara seperti di atas . Selain itu, kaedah mediaType () digunakan untuk membuat siri objek data Java ke format teks yang sesuai.

Secara lalai, jenis media bidang data adalah teks / biasa . Oleh itu, ia tidak perlu dinyatakan secara jelas ketika berurusan dengan jenis data String .

Jika tidak, jika kita ingin menangani objek tersuai, kita perlu menentukan jenis media atau memberikan MessageBodyWriter tersuai . JAX RS Runtime menyediakan MessageBodyWriters untuk jenis media yang paling terkenal .

Contoh Sse juga mempunyai jalan pintas dua pembangun untuk membuat acara dengan hanya bidang data, atau jenis dan bidang data:

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

3.4. Menghantar Acara Ringkas

Sekarang kita tahu bagaimana membina acara dan kita memahami bagaimana SSE Resource berfungsi. Mari hantar acara sederhana.

Antara muka SseEventSink mengaburkan sambungan HTTP tunggal. JAX-RS Runtime dapat menyediakannya hanya melalui suntikan dalam kaedah sumber SSE.

Menghantar acara semudah meminta SseEventSink. hantar ().

Dalam contoh seterusnya akan menghantar banyak kemas kini stok dan akhirnya akan menutup aliran acara:

@GET @Path("prices") @Produces("text/event-stream") public void getStockPrices(@Context SseEventSink sseEventSink /*..*/) { int lastEventId = //..; while (running) { 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) .comment("price change") .build(); sseEventSink.send(sseEvent); lastEventId++; } //.. } sseEventSink.close(); }

Setelah menghantar semua acara, pelayan menutup sambungan sama ada dengan menggunakan kaedah tutup () secara eksplisit atau, lebih baik, dengan menggunakan sumber daya mencuba, kerana SseEventSink memperluas antara muka AutoClosable :

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

In our sample app we can see this running if we visit:

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

3.5. Broadcasting Events

Broadcasting is the process by which events are sent to multiple clients simultaneously. This is accomplished by the SseBroadcaster API, and it is done in three simple steps:

First, we create the SseBroadcaster object from an injected Sse context as shown previously:

SseBroadcaster sseBroadcaster = sse.newBroadcaster();

Then, clients should subscribe to be able to receive Sse Events. This is generally done in an SSE resource method where a SseEventSink context instance is injected:

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

And finally, we can trigger the event publishing by invoking the broadcast() method:

@GET @Path("publish") public void broadcast() { OutboundSseEvent sseEvent = //...; this.sseBroadcaster.broadcast(sseEvent); }

This will send the same event to each registered SseEventSink.

To showcase the broadcasting, we can access this URL:

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

And then we can trigger the broadcasting by invoking the broadcast() resource method:

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

4. Consuming SSE Events

To consume an SSE event sent by the server, we can use any HTTP client, but for this tutorial, we'll use the JAX RS client API.

4.1. JAX RS Client API for SSE

To get started with the client API for SSE, we need to provide dependencies for JAX RS Client implementation.

Here, we'll use Apache CXF client implementation:

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

The SseEventSource is the heart of this API, and it is constructed from The WebTarget.

We start by listening for incoming events whose are abstracted by the InboundSseEvent interface:

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

Setelah sambungan dibuat, pengguna acara yang didaftarkan akan dipanggil untuk setiap InboundSseEvent yang diterima .

Kita kemudian boleh menggunakan kaedah readData () untuk membaca rentetan data asal :

String data = inboundSseEvent.readData();

Atau kita dapat menggunakan versi yang terlalu banyak untuk mendapatkan Objek Deserialized Java menggunakan jenis media yang sesuai:

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

Di sini, kami hanya menyediakan pengguna acara ringkas yang mencetak acara masuk di konsol.

5. Kesimpulan

Dalam tutorial ini, kami memfokuskan pada cara menggunakan Acara yang Dihantar Pelayan di JAX RS 2.1. Kami memberikan contoh yang menunjukkan cara menghantar acara kepada pelanggan tunggal dan juga cara menyiarkan acara kepada pelanggan berlipat kali ganda.

Akhirnya, kami menghabiskan acara ini menggunakan API klien JAX-RS.

Seperti biasa, kod tutorial ini boleh didapati di Github.