1前書き

ここ数年の間に、Javaでアプリケーションを作成する機能的かつ反応的な方法が登場しました。 Ratpackは同じ方向に沿ってHTTPアプリケーションを作成する方法を提供します。

ネットワーキングのニーズにNettyを使用しているため、

完全に非同期でノンブロッキングです

。 Ratpackはまた、コンパニオンテストライブラリを提供することによってテストのサポートを提供します。

このチュートリアルでは、Ratpack HTTPクライアントと関連コンポーネントの使用方法について説明します。

そうすることで、https://www.baeldung.com/ratpack[はじめにRatpackチュートリアル]の最後に残ったところから、理解をさらに深めていきます。


2 Mavenの依存関係

はじめに、必要なhttps://search.maven.org/search?q=io.ratpack[ラットパックの依存関係]を追加しましょう。

<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-core</artifactId>
    <version>1.5.4</version>
</dependency>
<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-test</artifactId>
    <version>1.5.4</version>
    <scope>test</scope>
</dependency>

興味深いことに、アプリケーションを作成しテストするために必要なのはこれだけです。

ただし、他のRatpackライブラリを使用していつでも追加と拡張を選択できます。


3バックグラウンド

私たちが飛び込む前に、Ratpackアプリケーションで物事が行われる方法について頭を上げましょう。


3.1. ハンドラベースのアプローチ

Ratpackはリクエスト処理にハンドラベースのアプローチを使います。考え自体は十分に単純です。

そして、その最も単純な形式では、それぞれの特定のパスでリクエストを処理する各ハンドラを持つことができます。

public class FooHandler implements Handler {
    @Override
    public void handle(Context ctx) throws Exception {
        ctx.getResponse().send("Hello Foo!");
    }
}


3.2. チェーン、レジストリ、およびコンテキスト

  • ハンドラはContextオブジェクトを使って入ってくるリクエストとやり取りします** それを通して、HTTPリクエストとレスポンスへのアクセス、そして他のハンドラに委譲する機能を手に入れます。

たとえば、次のようなハンドラを考えます。

Handler allHandler = context -> {
    Long id = Long.valueOf(context.getPathTokens().get("id"));
    Employee employee = new Employee(id, "Mr", "NY");
    context.next(Registry.single(Employee.class, employee));
};

このハンドラは何らかの前処理を行い、結果をR

__egistry

__に入れてから、その要求を他のハンドラに委任します。


  • Registry

    を使用することで、ハンドラ間通信** を実現できます。次の

    handler

    は、オブジェクト型を使用して

    Registry

    から以前に計算された結果を照会します。

Handler empNameHandler = ctx -> {
    Employee employee = ctx.get(Employee.class);
    ctx.getResponse()
      .send("Name of employee with ID " + employee.getId() + " is " + employee.getName());
};

プロダクションアプリケーションでは、これらのハンドラを抽象化、デバッグ、および複雑なビジネスロジックの開発のための個別のクラスとして使用することに注意してください。

  • これで、複雑なカスタムリクエスト処理パイプラインを作成するために、これらのハンドラを

    __Chain

    __in内で使用できます。

例えば:

Action<Chain> chainAction = chain -> chain.prefix("employee/:id", empChain -> {
    empChain.all(allHandler)
      .get("name", empNameHandler)
      .get("title", empTitleHandler);
});


Chain



insert(..)

メソッドを使用して複数のチェーンをまとめて構成し、それぞれに異なる懸念を持たせることで、このアプローチをさらに進めることができます。

次のテストケースでは、これらの構成要素の使用方法を示します。

@Test
public void givenAnyUri__GetEmployeeFromSameRegistry() throws Exception {
    EmbeddedApp.fromHandlers(chainAction)
      .test(testHttpClient -> {
          assertEquals("Name of employee with ID 1 is NY", testHttpClient.get("employee/1/name")
            .getBody()
            .getText());
          assertEquals("Title of employee with ID 1 is Mr", testHttpClient.get("employee/1/title")
            .getBody()
            .getText());
      });
}

ここでは、実際のサーバーを起動することなく、Ratpackのテストライブラリを使用して機能を単独でテストしています。


4 Ratpackを使ったHTTP


4.1. 非同期に向けての取り組み

HTTPプロトコルは本質的に同期です。その結果、多くの場合、Webアプリケーションは同期しているため、ブロックされます。着信リクエストごとにスレッドを作成するので、これは非常にリソース集約型のアプローチです。

ノンブロッキングで非同期のアプリケーションを作成したいと思います。これにより、リクエストを処理するためにスレッドの小さなプールを使用するだけで済みます。


4.2. コールバック関数

  • 非同期APIを扱う場合、通常、データを呼び出し元に返すことができるように、受信側にコールバック関数を提供します。** Javaでは、これは通常、無名の内部クラスとラムダ式の形式を取ります。しかし、私たちのアプリケーションの規模が拡大するにつれて、あるいは複数の非同期呼び出しがネストされているため、そのような解決策を維持するのは難しくなり、デバッグするのが難しくなります。

Ratpackは、この複雑さを

__Promise

__sの形式で処理するための洗練されたソリューションを提供します。


4.3. ラットパックの約束

RatpackのPromiseは、JavaのFutureオブジェクトに似ていると見なすことができます。

それは本質的には後で利用可能になる値の表現です。**

値が使用可能になったときに通過する操作のパイプラインを指定できます。各操作は、新しいpromiseオブジェクト、つまり前のpromiseオブジェクトを変換したものを返します。

ご想像のとおり、これはスレッド間のコンテキスト切り替えがほとんどなくなり、アプリケーションを効率的にします。

以下は

Promise

を利用するハンドラの実装です。

public class EmployeeHandler implements Handler {
    @Override
    public void handle(Context ctx) throws Exception {
        EmployeeRepository repository = ctx.get(EmployeeRepository.class);
        Long id = Long.valueOf(ctx.getPathTokens().get("id"));
        Promise<Employee> employeePromise = repository.findEmployeeById(id);
        employeePromise.map(employee -> employee.getName())
          .then(name -> ctx.getResponse()
          .send(name));
    }
}


  • promise

    は、最終的な値** をどうするかを定義するときに特に便利です。それには端末操作____then(Action)を呼び出すことでできます。

約束を返送する必要があるがデータソースが同期的である場合は、それでも可能です

@Test
public void givenSyncDataSource__GetDataFromPromise() throws Exception {
    String value = ExecHarness.yieldSingle(execution -> Promise.sync(() -> "Foo"))
      .getValueOrThrow();
    assertEquals("Foo", value);
}


4.4. HTTPクライアント

Ratpackは非同期HTTPクライアントを提供し、そのインスタンスはサーバーレジストリから取得できます。ただし、

デフォルトでは接続プールを使用せず、非常に保守的なデフォルトが設定されているため、

代替インスタンスを作成して使用することをお勧めします。


HttpClientSpec.

型の

Action

をパラメータとしてとる

of(Action)

メソッドを使用してインスタンスを作成できます。

これを使用して、クライアントを好みに合わせて調整できます。

HttpClient httpClient = HttpClient.of(httpClientSpec -> {
    httpClientSpec.poolSize(10)
      .connectTimeout(Duration.of(60, ChronoUnit.SECONDS))
      .maxContentLength(ServerConfig.DEFAULT__MAX__CONTENT__LENGTH)
      .responseMaxChunkSize(16384)
      .readTimeout(Duration.of(60, ChronoUnit.SECONDS))
      .byteBufAllocator(PooledByteBufAllocator.DEFAULT);
});

非同期の性質から推測したように、

HttpClient



Promise

オブジェクトを返します。その結果、非ブロッキング方式で複雑なオペレーションのパイプラインを構築できます。

例として、この

HttpClient

を使用して、クライアントに

EmployeeHandler

を呼び出すようにしましょう。

public class RedirectHandler implements Handler {

    @Override
    public void handle(Context ctx) throws Exception {
        HttpClient client = ctx.get(HttpClient.class);
        URI uri = URI.create("http://localhost:5050/employee/1");
        Promise<ReceivedResponse> responsePromise = client.get(uri);
        responsePromise.map(response -> response.getBody()
          .getText()
          .toUpperCase())
          .then(responseText -> ctx.getResponse()
            .send(responseText));
    }
}

素早いhttps://www.baeldung.com/curl-rest[cURL]電話は我々が期待された応答を得たことを確認するでしょう:

curl http://localhost:5050/redirect
JANE DOE


5結論

この記事では、Ratpackで利用可能なノンブロッキングおよび非同期Webアプリケーションの開発を可能にする主要なライブラリー構成について説明しました。

私たちはRatpackの

__HttpClient


とそれに付随するRatpackのすべての非同期を表す


Promise

__classを見ました。

また、付属の

TestHttpClient

を使用して、HTTPアプリケーションを簡単にテストする方法も説明しました。

そして、いつものように、このチュートリアルのコードスニペットは私たちのhttps://github.com/eugenp/tutorials/tree/master/ratpack[GitHub repository]で入手できます。