Finagleの紹介
1. 概要
このチュートリアルでは、TwitterのRPCライブラリであるFinagleについて簡単に説明します。
これを使用して、単純なクライアントとサーバーを構築します。
2. ビルディングブロック
実装を掘り下げる前に、アプリケーションの構築に使用する基本的な概念を理解する必要があります。 それらは広く知られていますが、Finagleの世界ではわずかに異なる意味を持つ可能性があります。
2.1. サービス
サービスは、リクエストを受け取り、操作の最終結果または障害に関する情報を含むFutureを返すクラスによって表される関数です。
2.2. フィルタ
フィルタも関数です。 彼らはリクエストとサービスを受け取り、リクエストに対していくつかの操作を実行し、それをサービスに渡し、結果の Future に対していくつかの操作を実行し、最後に最終的なFutureを返します。 これらは、関数の実行の前後で発生するロジックを実装し、その入力と出力を変更できるため、アスペクトと考えることができます。
2.3. 先物
Futuresは、非同期操作の最終的な結果を表します。 それらは、保留中、成功、または失敗の3つの状態のいずれかにある可能性があります。
3. サービス
まず、単純なHTTPgreeting serviceを実装します。 リクエストからnameパラメータを取得して応答し、通常の「Hello」メッセージを追加します。
そのためには、Finagleライブラリから抽象 Service クラスを拡張するクラスを作成し、そのapplyメソッドを実装する必要があります。
私たちが行っていることは、機能インターフェイスの実装に似ています。 ただし、興味深いことに、FinagleはScalaで記述されており、Java-Scalaの相互運用性を利用しているため、この特定の機能を実際に使用することはできません。
public class GreetingService extends Service<Request, Response> {
@Override
public Future<Response> apply(Request request) {
String greeting = "Hello " + request.getParam("name");
Reader<Buf> reader = Reader.fromBuf(new Buf.ByteArray(greeting.getBytes(), 0, greeting.length()));
return Future.value(Response.apply(request.version(), Status.Ok(), reader));
}
}
4. フィルター
次に、リクエストに関するデータをコンソールに記録するフィルターを作成します。 Service と同様に、Filterのapplyメソッドを実装する必要があります。このメソッドは、リクエストを受け取り、Futureレスポンスを返しますが今回は、サービスも2番目のパラメーターとして使用します。
基本的なFilterクラスには4つの型パラメーターがありますが、多くの場合、フィルター内の要求と応答の型を変更する必要はありません。
そのために、4つのタイプパラメータを2つにマージするSimpleFilterを使用します。 リクエストからいくつかの情報を出力してから、提供されたサービスからapplyメソッドを呼び出すだけです。
public class LogFilter extends SimpleFilter<Request, Response> {
@Override
public Future apply(Request request, Service<Request, Response> service) {
logger.info("Request host:" + request.host().getOrElse(() -> ""));
logger.info("Request params:");
request.getParams().forEach(entry -> logger.info("\t" + entry.getKey() + " : " + entry.getValue()));
return service.apply(request);
}
}
5. サーバ
これで、サービスとフィルターを使用して、実際に要求をリッスンして処理するサーバーを構築できます。
このサーバーに、andThenメソッドでチェーンされたフィルターとサービスの両方を含むサービスをプロビジョニングします。
Service serverService = new LogFilter().andThen(new GreetingService());
Http.serve(":8080", serverService);
6. クライアント
最後に、サーバーにリクエストを送信するクライアントが必要です。
そのために、FinagleのHttpクラスの便利なnewServiceメソッドを使用してHTTPサービスを作成します。 リクエストの送信は直接の責任となります。
さらに、以前に実装したものと同じロギングフィルターを使用し、HTTPサービスとチェーンします。 次に、applyメソッドを呼び出す必要があります。
その最後の操作は非同期であり、その最終的な結果はFutureインスタンスに保存されます。この Future が成功または失敗するのを待つことができますが、それはブロッキング操作になるため、それを避けてください。 代わりに、Futureが成功したときにトリガーされるコールバックを実装できます。
Service<Request, Response> clientService = new LogFilter().andThen(Http.newService(":8080"));
Request request = Request.apply(Method.Get(), "/?name=John");
request.host("localhost");
Future<Response> response = clientService.apply(request);
Await.result(response
.onSuccess(r -> {
assertEquals("Hello John", r.getContentString());
return BoxedUnit.UNIT;
})
.onFailure(r -> {
throw new RuntimeException(r);
})
);
BoxedUnit.UNITを返すことに注意してください。Unit を返すことは、 void メソッドに対処するScalaの方法であるため、相互運用性を維持するためにここで行います。
7. 概要
このチュートリアルでは、Finagleを使用して単純なHTTPサーバーとクライアントを構築する方法と、それらの間の通信を確立してメッセージを交換する方法を学びました。
いつものように、すべての例を含むソースコードは、GitHubのにあります。