Peristiwa Terkirim Server (SSE) Di JAX-RS

1. Ikhtisar

Server-Sent Events (SSE) adalah spesifikasi berbasis HTTP yang menyediakan cara untuk membuat sambungan saluran tunggal dan jangka panjang dari server ke klien.

Klien memulai koneksi SSE dengan menggunakan media jenis teks / event-stream di header Terima .

Nanti, itu diperbarui secara otomatis tanpa meminta server.

Kami dapat memeriksa lebih detail tentang spesifikasi di spesifikasi resmi.

Dalam tutorial ini, kami akan memperkenalkan implementasi SSE JAX-RS 2.1 yang baru.

Karenanya, kita akan melihat bagaimana kita dapat mempublikasikan acara dengan API Server JAX-RS. Juga, kita akan menjelajahi bagaimana kita dapat menggunakannya baik dengan JAX-RS Client API atau hanya dengan klien HTTP seperti alat curl .

2. Memahami Peristiwa SSE

Peristiwa SSE adalah blok teks yang terdiri dari bidang berikut:

  • Acara: jenis acara. Server dapat mengirim banyak pesan dari jenis yang berbeda dan klien hanya dapat mendengarkan jenis tertentu atau dapat memproses secara berbeda setiap jenis peristiwa
  • Data: pesan yang dikirim oleh server. Kami dapat memiliki banyak jalur data untuk acara yang sama
  • Id: id acara, digunakan untuk mengirim header Last-Event-ID , setelah coba lagi koneksi. Ini berguna karena dapat mencegah server mengirim acara yang sudah terkirim
  • Coba lagi: waktu, dalam milidetik, bagi klien untuk membuat sambungan baru saat arus hilang. Id yang diterima terakhir akan secara otomatis dikirim melalui header Last-Event-ID
  • ' : ': ini adalah komentar dan diabaikan oleh klien

Juga, dua kejadian berurutan dipisahkan oleh garis baru ganda ' \ n \ n '.

Selain itu, data dalam event yang sama dapat ditulis dalam banyak baris seperti yang dapat dilihat pada 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 diabstraksi oleh antarmuka SseEvent , atau lebih tepatnya, oleh dua subinterfaces OutboundSseEvent dan InboundSseEvent.

Sementara OutboundSseEvent digunakan pada API Server dan mendesain peristiwa terkirim, InboundSseEvent digunakan oleh API Klien dan mengabstraksi peristiwa yang diterima .

3. Menerbitkan Acara SSE

Sekarang kita telah membahas apa itu peristiwa SSE, mari kita lihat bagaimana kita dapat membangun dan mengirimkannya ke klien HTTP.

3.1. Pengaturan Proyek

Kami sudah memiliki tutorial tentang menyiapkan proyek Maven berbasis JAX RS. Jangan ragu untuk melihat di sana untuk mengetahui cara mengatur dependensi dan memulai dengan JAX RS.

3.2. Metode Sumber Daya SSE

Metode Sumber Daya SSE adalah metode JAX RS yang:

  • Dapat menghasilkan jenis media text / event-stream
  • Memiliki parameter SseEventSink yang dimasukkan , tempat peristiwa dikirim
  • Mungkin juga memiliki parameter Sse yang dimasukkan yang digunakan sebagai titik masuk untuk membuat pembuat 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 header HTTP berikut:

Accept: text/event-stream 

3.3. Mesin Virtual SSE

Instance SSE adalah kacang konteks yang akan disediakan JAX RS Runtime untuk injeksi.

Kami dapat menggunakannya sebagai pabrik untuk membuat:

  • OutboundSseEvent.Builder - memungkinkan kita membuat acara nanti
  • SseBroadcaster - memungkinkan kami menyiarkan acara ke banyak pelanggan

Mari kita lihat cara kerjanya:

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

Sekarang, mari fokus pada pembuat acara. OutboundSseEvent.Builder bertanggung jawab 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 bisa kita lihat, pembuat memiliki metode untuk menyetel nilai untuk semua bidang peristiwa yang ditunjukkan di atas . Selain itu, metode mediaType () digunakan untuk membuat serial objek Java bidang data ke format teks yang sesuai.

Secara default, jenis media dari bidang data adalah teks / biasa . Karenanya, ini tidak perlu ditentukan secara eksplisit saat berhadapan dengan tipe data String .

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

Instance Sse juga memiliki dua pintasan pembuat untuk membuat acara hanya dengan bidang data, atau bidang tipe dan data:

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

3.4. Mengirim Acara Sederhana

Sekarang kita tahu cara membuat acara dan memahami cara kerja Sumber Daya SSE. Ayo kirim acara sederhana.

The SseEventSink antarmuka abstrak koneksi HTTP tunggal. Waktu Proses JAX-RS dapat membuatnya tersedia hanya melalui injeksi dalam metode sumber daya SSE.

Mengirim acara semudah menjalankan SseEventSink. Kirim().

Dalam contoh berikutnya akan mengirim banyak pembaruan saham dan pada 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 mengirim semua peristiwa, server menutup koneksi baik dengan secara eksplisit memanggil metode close () atau, lebih disukai, dengan menggunakan coba-dengan-sumber daya, karena SseEventSink memperluas antarmuka 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 koneksi dibuat, konsumen acara terdaftar akan dipanggil untuk setiap InboundSseEvent yang diterima .

Kita kemudian bisa menggunakan metode readData () untuk membaca data asli String:

String data = inboundSseEvent.readData();

Atau kita dapat menggunakan versi yang kelebihan beban untuk mendapatkan Objek Java Deserialized menggunakan jenis media yang sesuai:

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

Di sini, kami hanya menyediakan konsumen acara sederhana yang mencetak acara yang masuk di konsol.

5. Kesimpulan

Dalam tutorial ini, kami fokus pada bagaimana menggunakan Server-Sent Events di JAX RS 2.1. Kami memberikan contoh yang menunjukkan cara mengirim acara ke satu klien serta cara menyiarkan acara ke beberapa klien.

Terakhir, kami menggunakan peristiwa ini menggunakan API klien JAX-RS.

Seperti biasa, kode tutorial ini dapat ditemukan di Github.