akka-http
Akka HTTPの概要
1. 概要
このチュートリアルでは、Akkaのlink:/akka-actors-java[Actor]&link:/akka-streams[Stream]モデルの助けを借りて学習します基本的なCRUD操作を提供するHTTP APIを作成するためにAkkaをセットアップする方法。
2. Mavenの依存関係
まず、Akka HTTPの使用を開始するために必要な依存関係を見てみましょう。
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-http_2.12</artifactId>
<version>10.0.11</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-stream_2.12</artifactId>
<version>2.5.11</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-http-jackson_2.12</artifactId>
<version>10.0.11</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-http-testkit_2.12</artifactId>
<version>10.0.11</version>
<scope>test</scope>
</dependency>
もちろん、これらのAkkaライブラリの最新バージョンはhttps://search.maven.org/search?q=com.typesafe.akka[Maven Central]で見つけることができます。
3. アクターを作成する
例として、ユーザーリソースを管理できるHTTP APIを作成します。 APIは2つの操作をサポートします。
-
新しいユーザーを作成する
-
既存のユーザーの読み込み
HTTP APIを提供する前に、*必要な操作を提供するアクターを実装する必要があります:*
class UserActor extends AbstractActor {
private UserService userService = new UserService();
static Props props() {
return Props.create(UserActor.class);
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(CreateUserMessage.class, handleCreateUser())
.match(GetUserMessage.class, handleGetUser())
.build();
}
private FI.UnitApply<CreateUserMessage> handleCreateUser() {
return createUserMessage -> {
userService.createUser(createUserMessage.getUser());
sender()
.tell(new ActionPerformed(
String.format("User %s created.", createUserMessage.getUser().getName())), getSelf());
};
}
private FI.UnitApply<GetUserMessage> handleGetUser() {
return getUserMessage -> {
sender().tell(userService.getUser(getUserMessage.getUserId()), getSelf());
};
}
}
基本的に、_AbstractActor_クラスを拡張し、その_createReceive()_メソッドを実装しています。
_createReceive()_内では、それぞれのタイプのメッセージを処理するメソッドに*着信メッセージタイプ*をマッピングしています。
*メッセージタイプは、特定の操作を説明するいくつかのフィールドを持つ単純なシリアライズ可能なコンテナクラスです*。 _GetUserMessage_には、ロードするユーザーを識別する単一のフィールド_userId_があります。 _CreateUserMessage_には、新しいユーザーを作成するために必要なユーザーデータを含む_User_オブジェクトが含まれます。
後で、着信HTTP要求をこれらのメッセージに変換する方法について説明します。
最終的に、すべてのメッセージを_UserService_インスタンスに委任します。これにより、永続的なユーザーオブジェクトの管理に必要なビジネスロジックが提供されます。
また、_props()_メソッドにも注意してください。 * __props()__methodは拡張には必要ありませんが、_ * AbstractActor *、_は後で_ActorSystem_を作成するときに役立ちます。
俳優についてのより詳細な議論については、https://www.baeldung.com/akka-actors-java [Akka Actorsの紹介]をご覧ください。
4. HTTPルートの定義
実際の作業を行うアクターがいるので、あとは、着信HTTP要求をアクターに委任するHTTP APIを提供するだけです。
Akkaは、ルートの概念を使用してHTTP APIを記述します。 *各操作には、ルートが必要です。*
HTTPサーバーを作成するには、フレームワーククラス_HttpApp_を拡張し、_routes_メソッドを実装します。
class UserServer extends HttpApp {
private final ActorRef userActor;
Timeout timeout = new Timeout(Duration.create(5, TimeUnit.SECONDS));
UserServer(ActorRef userActor) {
this.userActor = userActor;
}
@Override
public Route routes() {
return path("users", this::postUser)
.orElse(path(segment("users").slash(longSegment()), id -> route(getUser(id))));
}
private Route getUser(Long id) {
return get(() -> {
CompletionStage<Optional<User>> user =
PatternsCS.ask(userActor, new GetUserMessage(id), timeout)
.thenApply(obj -> (Optional<User>) obj);
return onSuccess(() -> user, performed -> {
if (performed.isPresent())
return complete(StatusCodes.OK, performed.get(), Jackson.marshaller());
else
return complete(StatusCodes.NOT_FOUND);
});
});
}
private Route postUser() {
return route(post(() -> entity(Jackson.unmarshaller(User.class), user -> {
CompletionStage<ActionPerformed> userCreated =
PatternsCS.ask(userActor, new CreateUserMessage(user), timeout)
.thenApply(obj -> (ActionPerformed) obj);
return onSuccess(() -> userCreated, performed -> {
return complete(StatusCodes.CREATED, performed, Jackson.marshaller());
});
})));
}
}
さて、ここにはかなりの定型文がありますが、**マッピング操作の前と同じパターン、今度はルートとして従うことに注意してください。 **少し分解しましょう。
_getUser()_内で、_GetUserMessage_型のメッセージで着信ユーザーIDをラップし、そのメッセージを_userActor_に転送します。
アクターがメッセージを処理すると、_onSuccess_ハンドラーが呼び出されます。このハンドラーでは、特定のHTTPステータスと特定のJSONボディを持つ応答を送信することで、HTTP要求を_complete_します。 link:/jackson-object-mapper-tutorial[Jackson] marshallerを使用して、アクターからの回答をJSON文字列にシリアル化します。
_postUser()_内では、HTTPリクエストにJSONボディが必要であるため、少し異なる方法で処理します。 _entity()_メソッドを使用して、着信JSONボディを_User_オブジェクトにマッピングしてから、_CreateUserMessage_にラップしてアクターに渡します。 ここでも、Jacksonを使用してJavaとJSONの間のマッピングを行います。
-
_HttpApp_は単一の_Route_オブジェクトを提供することを期待しているため、_routes_メソッド内で両方のルートを単一の_Route_に結合します。
_postUser()_によって提供されるルートをパス_ / users_にバインドします。 着信リクエストがPOSTリクエストではない場合、Akkaは自動的に_orElse_ブランチに入り、パスが_ / users / <id> _になることを期待します。 着信HTTPメソッドに応じて、要求は_getUser()_ routeに転送されるか、まだ一致しない場合はHTTPステータス404を返します。
AkkaでHTTPルートを定義する方法の詳細については、https://doc.akka.io/docs/akka-http/current/routing-dsl/routes.html [Akka docs]をご覧ください。
5. サーバーの起動
上記のような_HttpApp_実装を作成したら、数行のコードでHTTPサーバーを起動できます。
public static void main(String[] args) throws Exception {
ActorSystem system = ActorSystem.create("userServer");
ActorRef userActor = system.actorOf(UserActor.props(), "userActor");
UserServer server = new UserServer(userActor);
server.startServer("localhost", 8080, system);
}
*タイプ_UserActor_の単一のアクターで_ActorSystem_を作成し、_localhost_でサーバーを起動します。*
6. 結論
この記事では、Akka HTTPの基本について学び、HTTPサーバーをセットアップし、エンドポイントを公開して、REST APIと同様にリソースを作成およびロードする方法を示します。
いつものように、ここで紹介するソースコードはhttps://github.com/eugenp/tutorials/tree/master/akka-http[GitHub]で見つけることができます。