Scala: Akka-HTTP を試してみる
Akka-HTTP とは、Spray++ という位置づけの新進気鋭の HTTPサーバ フレームワーク。
Reactive Stream を使って実装されているのが大きな特徴。
Spray は ベンチマーク で Finagle や wai (Haskell) に圧勝している。
Akka-HTTP もその流れを引き継ぎ、ハイパフォーマンスサーバに適したフレームワークになるだろうと期待している。
これまで Spray すら使ったことがなかったのだが、少しだけ Akka-HTTP に触れてみる。
Hello World with Akka-HTTP
/ping にアクセスすると "PONG" を返すシンプルな Webサーバを作る。
project/Build.scala
import sbt._ import sbt.Keys._ object ApplicationBuild extends Build { lazy val buildSettings = Seq( organization := "com.github.mogproject", version := "0.1.0-SNAPSHOT", scalaVersion := "2.11.1", scalacOptions ++= Seq( "-deprecation", "-unchecked", "-feature", "-encoding", "utf-8" ), javacOptions ++= Seq( "-source", "1.7" ) ) lazy val resolverSettings = Seq( resolvers ++= Seq( "Sonatype OSS Releases" at "http://oss.sonatype.org/content/repositories/releases", "Sonatype OSS Snapshots" at "http://oss.sonatype.org/content/repositories/snapshots", "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/" ), libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % "2.2.0" % "test", "com.typesafe" % "config" % "1.2.1", "com.typesafe.akka" %% "akka-http-core-experimental" % "0.4", "com.typesafe.akka" %% "akka-stream-experimental" % "0.4" ) ) lazy val testSettings = Seq( parallelExecution in Test := true ) lazy val root = Project( id = "akka-http-example", base = file("."), settings = super.settings ++ buildSettings ++ resolverSettings ++ testSettings ) }
src/main/resources/application.conf
akka { loglevel = INFO }
src/main/scala/Boot.scala
- Scala Days 2014 資料のサンプルコード そのままでは型推論周りでコンパイルエラーとなった。
import akka.io.IO import akka.http.Http import akka.actor.ActorSystem import akka.pattern.ask import akka.util.Timeout import akka.http.model._ import akka.http.model.HttpMethods._ import scala.concurrent.duration._ import akka.stream.scaladsl.Flow object Boot extends App { import system.dispatcher implicit val system = ActorSystem() implicit val askTimeout: Timeout = 500.millis val bindingFuture = IO(Http) ? Http.Bind("localhost", 8080) bindingFuture foreach { binding => Flow(binding.connectionStream) foreach { connection => Flow(connection.requestProducer).map { case HttpRequest(GET, Uri.Path("/ping"), _, _, _) => HttpResponse(entity = "PONG!") case _ => HttpResponse(404, entity = "Unknown resource!") }.produceTo(connection.responseOut) } } }
[error] akka-http-example/src/main/scala/Boot.scala:20: value connectionStream is not a member of Any [error] Flow(binding.connectionStream) foreach { connection ⇒ [error] ^ [error] one error found [error] (compile:compile) Compilation failed
- 以下のように修正したら、動いた
import akka.io.IO import akka.http.Http import akka.actor.ActorSystem import akka.pattern.ask import akka.util.Timeout import akka.stream.{MaterializerSettings, FlowMaterializer} import akka.http.model._ import akka.http.model.HttpMethods._ import scala.concurrent.duration._ import akka.stream.scaladsl.Flow object Boot extends App { import system.dispatcher implicit val system = ActorSystem() implicit val askTimeout: Timeout = 500.millis val materializer = FlowMaterializer(MaterializerSettings()) val bindingFuture = IO(Http) ? Http.Bind("localhost", 8080) bindingFuture foreach { case Http.ServerBinding(localAddress, connectionStream) => Flow(connectionStream).foreach { case Http.IncomingConnection(remoteAddress, requestProducer, responseConsumer) => Flow(requestProducer).map { case HttpRequest(GET, Uri.Path("/ping"), _, _, _) => HttpResponse(entity = "PONG!") case _ => HttpResponse(404, entity = "Unknown resource!") }.produceTo(materializer, responseConsumer) }.consume(materializer) } }
$ curl http://localhost:8080/ping PONG! $ curl http://localhost:8080/pin Unknown resource!
ルーティング
ロードマップによると、spray-routing に相当する DSLライブラリが akka-http (akka-http-core ではない) に組み込まれる予定とのこと。
現時点ではまだ無いのでパターンマッチで何とかするしかなさそう。
- 公式ドキュメントにかかれていたサンプル
import HttpMethods._ val requestHandler: HttpRequest ⇒ HttpResponse = { case HttpRequest(GET, Uri.Path("/"), _, _, _) ⇒ HttpResponse( entity = HttpEntity(MediaTypes.`text/html`, "<html><body>Hello world!</body></html>")) case HttpRequest(GET, Uri.Path("/ping"), _, _, _) ⇒ HttpResponse(entity = "PONG!") case HttpRequest(GET, Uri.Path("/crash"), _, _, _) ⇒ sys.error("BOOM!") case _: HttpRequest ⇒ HttpResponse(404, entity = "Unknown resource!") }
0 件のコメント:
コメントを投稿