1概要

  • Server-Sent Events(SSE)はHTTPベースの仕様で、サーバーからクライアントへの長時間のモノチャネル接続を確立する方法を提供します。 **

クライアントは、

Accept

ヘッダーのメディアタイプ

text/event-stream

を使用してSSE接続を開始します。

その後、サーバーを要求せずに自動的に更新されます。

仕様の詳細については、https://html.spec.whatwg.org/multipage/server-sent-events.html[公式仕様]を参照してください。

このチュートリアルでは、SSEの新しいJAX-RS 2.1実装を紹介します。

そのため、JAX-RSサーバーAPIを使用してイベントを公開する方法について説明します。また、JAX-RS Client APIまたは

curl

ツールのようなHTTPクライアントのどちらによってそれらを消費することができるかについても調べます。


2 SSEイベントについて

SSEイベントは、次のフィールドで構成されるテキストのブロックです。


  • Event:

    イベントの種類。サーバーは多くのメッセージを送ることができます

異なるタイプとクライアントは特定のタイプのみをリッスンするかもしれません。
イベントタイプごとに異なる処理が可能
**

Data:

サーバーによって送信されたメッセージ。私達は多くのデータラインを持つことができます

同じイベント
**

Id:

イベントのID。

Last-Event-ID

ヘッダーの送信に使用

接続再試行後それはサーバがから防ぐことができるので有用です
送信済みのイベントを送信する
**

Retry:

クライアントが新しいパスワードを確立するまでの時間(ミリ秒)

電流が失われたときの接続。最後に受信したIDは


_ Last-Event-ID


headerを介して自動的に送信されます。
**


:

_

:これはコメントであり、クライアントによって無視されます

また、2つの連続したイベントは二重改行「

\ n \ n

」で区切られています。

さらに、次の例に示すように、同じイベント内のデータを多数の行に書き込むことができます。

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}


JAX RS

では、SSEイベントは

SseEvent

インターフェース

、より正確には2つのサブインターフェース

OutboundSseEvent

および

InboundSseEvent.__によって抽象化されます。


  • OutboundSseEvent

    がサーバーAPIで使用され送信されたイベントを設計するのに対し、

    __ InboundSseEvent

    __はクライアントAPIによって使用され、受信したイベントを抽象化します** 。


3 SSEイベントの発行

SSEイベントとは何かを説明したので、それを作成してHTTPクライアントに送信する方法を見てみましょう。


3.1. プロジェクト設定

JAX RSベースのMavenプロジェクトの設定については、https://www.baeldung.com/jax-rs-spec-and-implementations[tutorial]をすでに持っています。依存関係を設定してJAX RSを使い始める方法については、ぜひご覧ください。


3.2. SSEリソース方式

SSEリソースメソッドは、以下のJAX RSメソッドです。

  • text/event-stream__メディアタイプを生成できます

  • イベントが送信される注入された

    SseEventSink

    パラメータがあります

  • エントリとして使用される注入された

    Sse

    パラメータもあるかもしれません

イベントビルダーを作成するためのポイント

@GET
@Path("prices")
@Produces("text/event-stream")
public void getStockPrices(@Context SseEventSink sseEventSink, @Context Sse sse) {
   //...
}

その結果、クライアントは次のHTTPヘッダを使用して最初のHTTP要求を行う必要があります。

Accept: text/event-stream


3.3. SSEインスタンス

SSEインスタンスは、JAX RS Runtimeがインジェクションに使用できるようにするコンテキストBeanです。

私たちはそれを作成するためのファクトリとして使うことができます:


  • __OutboundSseEvent.Builder –

    __ではイベントを作成できます


  • __SseBroadcaster –

    __はイベントを複数の人にブロードキャストすることを許可します

購読者

それがどのように機能するのかを見てみましょう。

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

それでは、イベントビルダーに注目しましょう。

OutboundSseEvent.Builder

は、

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();

ご覧のとおり、

ビルダーには

上記のすべてのイベントフィールドに値を設定するメソッドがあります。さらに、


_ mediaType()

_

methodは、データフィールドのJavaオブジェクトを適切なテキスト形式にシリアル化するために使用されます。

デフォルトでは、データフィールドのメディアタイプは

text/plain

です。したがって、

String

データ型を扱うときに明示的に指定する必要はありません。

そうではなく、カスタムオブジェクトを処理する場合は、メディアタイプを指定するか、カスタム__MessageBodyWriterを指定する必要があります。

Sseインスタンスには、データフィールドのみ、またはタイプフィールドとデータフィールドのみでイベントを作成するための2つのBuilderショートカットもあります。

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


3.4. 簡易イベントの送信

これでイベントの作成方法がわかったので、SSEリソースがどのように機能するのかを理解しました。簡単なイベントを送りましょう。


SseEventSink

インターフェースは単一のHTTP接続を抽象化します。 JAX-RSランタイムは、SSEリソースメソッドへの注入によってのみ使用可能にすることができます。

イベントの送信は、

SseEventSink.send()を呼び出すのと同じくらい簡単です。

次の例では、大量の株価情報を送信し、最終的にイベントストリームを閉じます。

@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();
}

すべてのイベントを送信した後、サーバーは

close()

メソッドを明示的に呼び出すか、または

SseEventSink



AutoClosable

インターフェースを拡張するときに

try-with-resource

を使用することによって、接続を閉じます。

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

私たちのサンプルアプリでは、次のようにアクセスすると、このランニングを見ることができます。

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


3.5. 放送イベント

ブロードキャストは、イベントが複数のクライアントに同時に送信されるプロセスです。これは

SseBroadcaster

APIによって行われ、3つの簡単なステップで行われます。

最初に、前に示したように、注入されたSseコンテキストから

SseBroadcaster

オブジェクトを作成します。

SseBroadcaster sseBroadcaster = sse.newBroadcaster();

その後、クライアントはSseイベントを受信できるように登録する必要があります。これは通常、

SseEventSink

コンテキストインスタンスがインジェクトされるSSEリソースメソッドで行われます。

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

そして最後に、**

broadcast()

メソッドを呼び出すことでイベントの発行を開始できます。

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

これは、登録された各__SseEventSinkに同じイベントを送ります。

放送を紹介するには、次のURLにアクセスします。

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

そして、broadcast()リソースメソッドを呼び出すことでブロードキャストをトリガーすることができます。

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


4 SSEイベントの消費

サーバーから送信されたSSEイベントを消費するには、任意のHTTPクライアントを使用できますが、このチュートリアルではJAX RSクライアントAPIを使用します。


4.1. SSE用JAX RSクライアントAPI

SSE用のクライアントAPIを使い始めるには、JAX RS Clientの実装に依存関係を提供する必要があります。

ここでは、Apache CXFクライアント実装を使用します。

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-rs-client</artifactId>
    <version>${cxf-version}</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-rs-sse</artifactId>
    <version>${cxf-version}</version>
</dependency>


SseEventSource

はこのAPIの核心であり、

WebTarget.

から構築されています。


InboundSseEvent

インターフェースによって抽象化された着信イベントをlistenすることから始めます。

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();
}

  • 接続が確立されると、登録されたイベントコンシューマは受信した各




    InboundSseEvent ** .に対して呼び出されます。

その後、__readData()メソッドを使用して元のデータを読み取ることができます。

String data = inboundSseEvent.readData();

あるいは、オーバーロードされたバージョンを使用して、適切なメディアタイプを使用して直列化復元されたJavaオブジェクトを取得することもできます。

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

ここでは、受信したイベントをコンソールに表示する単純なイベントコンシューマを提供しました。


5結論

このチュートリアルでは、JAX RS 2.1のServer-Sent Eventsの使い方に焦点を当てました。単一のクライアントにイベントを送信する方法と、複数のクライアントにイベントをブロードキャストする方法を紹介する例を示しました。

最後に、JAX-RSクライアントAPIを使用してこれらのイベントを消費しました。

いつものように、このチュートリアルのコードはhttps://github.com/eugenp/tutorials/tree/master/apache-cxf/sse-jaxrs[Githubに掲載]にあります。