アクターとつないでみる
次に、Akka HTTPで受け付けたリクエストに対する処理を非同期に行うため、アクターにつないでみます。以前作ったレジ係アクター(CashierActor)やバリスタアクター(BaristarActor)にメッセージを送ってみましょう。
アクターへのメッセージ送信
/orderへのリクエストがあるとPUTされたJSON形式のオーダーを、最終的にコーヒーを作るバリスタアクターに定義されたOrder型にアンマーシャリングし、レジ係アクターに送ります。その後、レジ係アクターからの結果を待つことなく受け付けを完了した旨レスポンスします。
アクターにメッセージを送信
レジ係アクターを生成し、サーバー起動時にレジ係アクターに初期化(Initialize)メッセージを送り、カフェをスタートさせます。
リスト16:HTTPサーバーの起動
02 | val cashierActor = system.actorOf(CashierActor.props, "cashierActor") |
05 | val bindingFuture = Http().bindAndHandle(route, "localhost", 8080) |
07 | .map { serverBinding => |
08 | log.info(s"Server online at ${serverBinding.localAddress}") |
10 | // 初期化メッセージを送信しカフェをスタートさせる |
11 | cashierActor ! CashierActor.Initialize |
JSON形式をバリスタアクターに定義されたOrder型にマーシャリング・アンマーシャリングするマーシャラーを定義します。
リスト17:マーシャラーの定義
1 | import cafe._3_tellpattern.BaristaActor.Order |
3 | // JSON形式とOrder型のマーシャリング・アンマーシャリング |
4 | implicit val orderFormat = jsonFormat2(Order) |
/orderへのPUTリクエストで送信されたJSON形式のオーダーを、Orderケースクラスにアンマーシャリングし、Orderメッセージをレジ係アクターに送信します。
リスト18:ルート定義
04 | log.info("received PUT /order") |
05 | entity(as[Order]) { order => |
06 | // レジ係アクターへOrderメッセージを送信 |
08 | complete(s"Received order: ${order.product} * ${order.count}") |
メッセージを送信すると、オーダーを受け付けたことを即座にレスポンスしました。
リスト19:オーダー受け付けのレスポンス
4 | Content-Type: text/plain; charset=UTF-8 |
5 | Date: Mon, 10 Sep 2018 08:16:20 GMT |
6 | Server: akka-http/10.1.5 |
8 | Received order: Coffee * 10 |
リクエスト/レスポンス
アクターからの処理結果をレスポンスとしてクライアントに応答したい場合は、askパターン(「?」)でメッセージを送ります。askパターンでメッセージを送信するとFutureを返すため、レジ係アクターから応答があったときにその内容をレスポンスするようにします。
リクエストに対するレスポンス
レジ係アクターにOrderを送信し、処理が正常に行われた場合はレジ係アクターからの完了メッセージをレスポンスとして返し、失敗した場合は例外メッセージをそのままレスポンスするようにします。
リスト20:ルート定義(アクターからの応答あり)
01 | import cafe._4_askpattern.CashierActor.OrderCompleted |
06 | entity(as[Order]) { order => |
07 | log.info("receive PUT request /order") |
09 | implicit val timeout: Timeout = 5.seconds |
11 | val response: Future[Any] = cashierActor ? order |
13 | // レジ係アクターからの応答を受信したときの処理 |
14 | onComplete(response.mapTo[OrderCompleted]) { |
15 | case Success(orderCompleted) => |
17 | complete(s"${orderCompleted.message}") |
20 | complete(s"${t.getMessage}") |
正常に処理された場合の実行結果は、次のようになります。
リスト21:実行結果
4 | Content-Type: text/plain; charset=UTF-8 |
5 | Date: Mon, 10 Sep 2018 08:18:32 GMT |
6 | Server: akka-http/10.1.5 |
前回までに作成したAkkaのアクター同士がメッセージを送受信するアプリケーションが、Akka HTTPを使うことでWeb APIとして注文を受け付けることのできるシンプルなWebアプリケーションになりました。
さいごに
今回はAkkaの拡張機能のひとつであるAkka HTTPを使ってサーバーを起動し、Web APIを作成する方法を紹介しました。フルスタックなWebアプリケーションフレームワークを使用せずに、簡単にAPIを作成できることがお分かりいただけたことでしょう。なお、本記事で紹介したサンプルコードは、GitHub上で公開しています。
参考文献