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 件のコメント:
コメントを投稿