VertxとRxJavaの統合の例

VertxとRxJavaの統合の例

1. 概要

RxJavaは、非同期のイベントベースのプログラムを作成するための人気のあるライブラリであり、Reactive Extensionsイニシアチブによって提唱された主要なアイデアからインスピレーションを得ています。

Eclipseの傘下にあるプロジェクトであるVert.xは、リアクティブパラダイムを完全に活用するためにゼロから設計されたいくつかのコンポーネントを提供します。

一緒に使用すると、リアクティブである必要があるJavaプログラムの有効な基盤になる可能性があります。

この記事では、都市名のリストを含むファイルを読み込み、日の出から日没までの1日の長さをそれぞれについて印刷します。

公開されているwww.metaweather.comREST APIから公開されたデータを使用して、日光の長さを計算し、RxJavaVert.xを使用して、純粋に反応的な方法で計算します。

2. メーベン依存

vertx-rx-java2をインポートすることから始めましょう:


    io.vertx
    vertx-rx-java2
    3.5.0-Beta1

執筆時点では、Vert.xと新しいRxJava 2の統合はベータリリースとしてのみ利用可能ですが、構築中のプログラムには十分安定しています。

io.vertx:vertx-rx-java2io.reactivex.rxjava2:rxjavaに依存しているため、RxJava関連のパッケージを明示的にインポートする必要はありません。

Vert.xRxJavaの統合の最新バージョンは、Maven Centralにあります。

3. セットアップ

Vert.x,を使用するすべてのアプリケーションと同様に、すべてのVert.x機能へのメインエントリポイントであるvertxオブジェクトの作成を開始します。

Vertx vertx = io.vertx.reactivex.core.Vertx.vertx();

vertx-rx-java2ライブラリは、io.vertx.core.Vertxio.vertx.reactivex.core.Vertxの2つのクラスを提供します。 前者はVert.xに一意に基づくアプリケーションの通常のエントリポイントですが、後者はRxJavaとの統合を取得するために使用する必要があるエントリポイントです。

後で使用するオブジェクトの定義に進みます。

FileSystem fileSystem = vertx.fileSystem();
HttpClient httpClient = vertx.createHttpClient();

Vert.xFileSystemはリアクティブな方法でファイルシステムへのアクセスを提供しますが、Vert.xHttpClientHTTPに対して同じことを行います。

4. 反応性チェーン

リアクティブコンテキストでは、いくつかのより単純なリアクティブ演算子を連結して、意味のある計算を取得するのは簡単です。

our exampleに対してそれを実行しましょう:

fileSystem
  .rxReadFile("cities.txt").toFlowable()
  .flatMap(buffer -> Flowable.fromArray(buffer.toString().split("\\r?\\n")))
  .flatMap(city -> searchByCityName(httpClient, city))
  .flatMap(HttpClientResponse::toFlowable)
  .map(extractingWoeid())
  .flatMap(cityId -> getDataByPlaceId(httpClient, cityId))
  .flatMap(toBufferFlowable())
  .map(Buffer::toJsonObject)
  .map(toCityAndDayLength())
  .subscribe(System.out::println, Throwable::printStackTrace);

次に、コードの各論理チャンクがどのように機能するかを調べてみましょう。

5. 都市名

最初のステップは、都市名のリストを含むファイルを読み取ることです。1行に1つの名前があります。

fileSystem
 .rxReadFile("cities.txt").toFlowable()
 .flatMap(buffer -> Flowable.fromArray(buffer.toString().split("\\r?\\n")))

メソッドrxReadFile()はリアクティブにファイルを読み取り、RxJavaSingle<Buffer>を返します。 これで、探している統合が得られました。RxJavaからのデータ構造におけるVert.xの非同期性です。

ファイルは1つしかないため、ファイルの全内容を含むBufferが1回出力されます。 その入力をRxJavaFlowableに変換し、ファイルの行をフラットマップして、代わりに各都市名のイベントを発行するFlowableを作成します。

6. JSON City Descriptor

都市名を取得したら、次のステップはMetaweather REST APIを使用してその都市のIDコードを取得することです。 この識別子は、都市の日の出と日没の時刻を取得するために使用されます。 一連の呼び出しを続けましょう。

一連の呼び出しを続けましょう。

.flatMap(city -> searchByCityName(httpClient, city))
.flatMap(HttpClientResponse::toFlowable)

searchByCityName()メソッドは、最初のステップで作成したHttpClientを使用して、都市の識別子を提供するRESTサービスを呼び出します。 次に、2番目のflatMap(),で、応答を含むBufferを取得します。

searchByCityName()の本体を記述して、このステップを完了しましょう。

Flowable searchByCityName(HttpClient httpClient, String cityName) {
    HttpClientRequest req = httpClient.get(
        new RequestOptions()
          .setHost("www.metaweather.com")
          .setPort(443)
          .setSsl(true)
          .setURI(format("/api/location/search/?query=%s", cityName)));
    return req
      .toFlowable()
      .doOnSubscribe(subscription -> req.end());
}

Vert.xHttpClientは、リアクティブなHTTP応答を発行するRxJavaFlowableを返します。 これは、Buffersで分割された応答の本体を発行します。

適切なURLへの新しいリアクティブリクエストを作成しましたが、Vert.x requires the HttpClientRequest.end() method to be invokedはリクエストを送信できることを通知し、end()を正常に呼び出すには少なくとも1つのサブスクリプションが必要であることに注意しました。

これを実現するための解決策は、RxJavadoOnSubscribe()を使用して、コンシューマーがサブスクライブするとすぐにend()を呼び出すことです。

7. 都市識別子

ここで、返されたJSONオブジェクトのwoeidプロパティの値を取得する必要があります。これは、カスタムメソッドを介して都市を一意に識別します。

.map(extractingWoeid())

extractingWoeid()メソッドは、RESTサービス応答に含まれるJSONから都市識別子を抽出する関数を返します。

private static Function extractingWoeid() {
    return cityBuffer -> cityBuffer
      .toJsonArray()
      .getJsonObject(0)
      .getLong("woeid");
}

Bufferが提供する便利なtoJson…()メソッドを使用して、必要なプロパティにすばやくアクセスできることに注意してください。

8. 都市の詳細

リアクティブチェーンを続行して、REST APIから必要な詳細を取得しましょう。

.flatMap(cityId -> getDataByPlaceId(httpClient, cityId))
.flatMap(toBufferFlowable())

getDataByPlaceId()メソッドの詳細を見てみましょう。

static Flowable getDataByPlaceId(
  HttpClient httpClient, long placeId) {

    return autoPerformingReq(
      httpClient,
      format("/api/location/%s/", placeId));
}

ここでは、前の手順で導入したのと同じアプローチを使用しました。 getDataByPlaceId()Flowable<HttpClientResponse>を返します。 HttpClientResponseは、数バイトより長い場合、API応答をチャンクで出力します。

toBufferFlowable()メソッドを使用して、応答チャンクを1つに減らし、完全なJSONオブジェクトにアクセスできるようにします。

static Function>
  toBufferFlowable() {
    return response -> response
      .toObservable()
      .reduce(
        Buffer.buffer(),
        Buffer::appendBuffer).toFlowable();
}

9. 日没と日の出の時間

JSONオブジェクトから関心のある情報を取得して、リアクティブチェーンに追加し続けましょう。

.map(toCityAndDayLength())

toCityAndDayLength()メソッドを書いてみましょう。

static Function toCityAndDayLength() {
    return json -> {
        ZonedDateTime sunRise = ZonedDateTime.parse(json.getString("sun_rise"));
        ZonedDateTime sunSet = ZonedDateTime.parse(json.getString("sun_set"));
        String cityName = json.getString("title");
        return new CityAndDayLength(
          cityName, sunSet.toEpochSecond() - sunRise.toEpochSecond());
    };
}

JSONに含まれる情報をマップして、日の出から日没までの時間を時間単位で単純に計算するPOJOを作成する関数を返します。

10. 購読

リアクティブチェーンが完了しました。 これで、CityAndDayLengthの発行されたインスタンスを出力するハンドラー、またはエラーの場合はスタックトレースを使用して、結果のFlowableをサブスクライブできます。

.subscribe(
  System.out::println,
  Throwable::printStackTrace)

アプリケーションを実行すると、リストに含まれる都市とアプリケーションが実行される日付に応じて、そのような結果が表示されます。

In Chicago there are 13.3 hours of light.
In Milan there are 13.5 hours of light.
In Cairo there are 12.9 hours of light.
In Moscow there are 14.1 hours of light.
In Santiago there are 11.3 hours of light.
In Auckland there are 11.2 hours of light.

HTTP APIへのすべての要求は非同期で実行されるため、都市はファイルで指定された順序とは異なる順序で表示される可能性があります。

11. 結論

この記事では、Vert.xリアクティブモジュールをRxJavaによって提供される演算子および論理構造と混合することがいかに簡単であるかを見ました。

長いとはいえ、構築したリアクティブチェーンは、複雑なシナリオをかなり簡単に作成できることを示しました。

いつものように、完全なソースコードはover on GitHubで利用できます。