Akka HTTPでWeb APIに仕立てる

2018年9月20日(木)
前出 祐吾
第9回目となる今回は、Akkaの拡張機能Akka HTTPを用いてWeb APIを作成する方法を紹介します。

アクターとつないでみる

次に、Akka HTTPで受け付けたリクエストに対する処理を非同期に行うため、アクターにつないでみます。以前作ったレジ係アクター(CashierActor)やバリスタアクター(BaristarActor)にメッセージを送ってみましょう。

アクターへのメッセージ送信

/orderへのリクエストがあるとPUTされたJSON形式のオーダーを、最終的にコーヒーを作るバリスタアクターに定義されたOrder型にアンマーシャリングし、レジ係アクターに送ります。その後、レジ係アクターからの結果を待つことなく受け付けを完了した旨レスポンスします。

アクターにメッセージを送信

アクターにメッセージを送信

レジ係アクターを生成し、サーバー起動時にレジ係アクターに初期化(Initialize)メッセージを送り、カフェをスタートさせます。

リスト16:HTTPサーバーの起動

// レジ係アクターを生成
val cashierActor = system.actorOf(CashierActor.props, "cashierActor")

// HTTPサーバーの起動
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
bindingFuture
  .map { serverBinding =>
    log.info(s"Server online at ${serverBinding.localAddress}")

    // 初期化メッセージを送信しカフェをスタートさせる
    cashierActor ! CashierActor.Initialize
    // ..

JSON形式をバリスタアクターに定義されたOrder型にマーシャリング・アンマーシャリングするマーシャラーを定義します。

リスト17:マーシャラーの定義

import cafe._3_tellpattern.BaristaActor.Order

// JSON形式とOrder型のマーシャリング・アンマーシャリング
implicit val orderFormat = jsonFormat2(Order)

/orderへのPUTリクエストで送信されたJSON形式のオーダーを、Orderケースクラスにアンマーシャリングし、Orderメッセージをレジ係アクターに送信します。

リスト18:ルート定義

val route: Route =
  path("order") {
    put {
      log.info("received PUT /order")
      entity(as[Order]) { order =>
        // レジ係アクターへOrderメッセージを送信
        cashierActor ! order
        complete(s"Received order: ${order.product} * ${order.count}")
      }
    }
  }

メッセージを送信すると、オーダーを受け付けたことを即座にレスポンスしました。

リスト19:オーダー受け付けのレスポンス

> http PUT http://localhost:8080/order product="Coffee" count:=10
HTTP/1.1 200 OK
Content-Length: 27
Content-Type: text/plain; charset=UTF-8
Date: Mon, 10 Sep 2018 08:16:20 GMT
Server: akka-http/10.1.5

Received order: Coffee * 10

リクエスト/レスポンス

アクターからの処理結果をレスポンスとしてクライアントに応答したい場合は、askパターン(「?」)でメッセージを送ります。askパターンでメッセージを送信するとFutureを返すため、レジ係アクターから応答があったときにその内容をレスポンスするようにします。

リクエストに対するレスポンス

リクエストに対するレスポンス

レジ係アクターにOrderを送信し、処理が正常に行われた場合はレジ係アクターからの完了メッセージをレスポンスとして返し、失敗した場合は例外メッセージをそのままレスポンスするようにします。

リスト20:ルート定義(アクターからの応答あり)

import cafe._4_askpattern.CashierActor.OrderCompleted

val route =
  put {
    path("order") {
      entity(as[Order]) { order =>
        log.info("receive PUT request /order")

        implicit val timeout: Timeout = 5.seconds
        // レジ係アクターへOrderを送信
        val response: Future[Any] = cashierActor ? order

        // レジ係アクターからの応答を受信したときの処理
        onComplete(response.mapTo[OrderCompleted]) {
          case Success(orderCompleted) =>
            // 成功した場合は完了メッセージを返す
            complete(s"${orderCompleted.message}")
          case Failure(t) =>
            // 失敗した場合は例外メッセージを返す
            complete(s"${t.getMessage}")
        }
      }
    }
  }

正常に処理された場合の実行結果は、次のようになります。

リスト21:実行結果

> http post http://localhost:8080/order product="Coffee" count:=10
HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain; charset=UTF-8
Date: Mon, 10 Sep 2018 08:18:32 GMT
Server: akka-http/10.1.5

ok

前回までに作成したAkkaのアクター同士がメッセージを送受信するアプリケーションが、Akka HTTPを使うことでWeb APIとして注文を受け付けることのできるシンプルなWebアプリケーションになりました。

さいごに

今回はAkkaの拡張機能のひとつであるAkka HTTPを使ってサーバーを起動し、Web APIを作成する方法を紹介しました。フルスタックなWebアプリケーションフレームワークを使用せずに、簡単にAPIを作成できることがお分かりいただけたことでしょう。なお、本記事で紹介したサンプルコードは、GitHub上で公開しています。

参考文献

TIS株式会社

生産技術R&D室所属。これまで社内向けWebアプリケーションフレームワークの開発などシステム開発の効率化に取り組んできた。現在はリアクティブシステムをエンタープライズの分野に適用するための技術検証を行う傍ら、Lightbendの認定コンサルティングパートナーとして、同技術を用いたPoCや開発の支援などに従事している。

リアクティブシステムが日本国内で広く活用されることでエンジニアが障害対応から解放されることを願い、執筆/講演活動、Akkaのドキュメント翻訳などにも取り組んでいる。

連載バックナンバー

設計/手法/テスト技術解説
第9回

Akka HTTPでWeb APIに仕立てる

2018/9/20
第9回目となる今回は、Akkaの拡張機能Akka HTTPを用いてWeb APIを作成する方法を紹介します。
開発言語
第8回

Akka Streamsで実装するリアクティブストリーム(その2)

2018/3/13
前回に引き続き、Akka Streamsを使って実装するリアクティブストリームについて解説する。
開発言語技術解説
第7回

Akka Streamsで実装するリアクティブストリーム

2018/3/7
今回と次回の2回に分けて、Akka Streamsを使って実装するリアクティブストリームについて解説する。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています