Akka-HTTP vs Spray ベンチマーク取ってみた

[ブログ](https://t-hiroyoshi.github.io/spray-akka-benchmark/)のコピペです。

今回は[前回紹介](http://t-hiroyoshi.github.io/akka-http-api/)した[Akka-HTTP](http://doc.akka.io/docs/akka/2.4.4/scala/http/introduction.html)の性能が気になったのでざっくりベンチマークを取ってみました。
ソースコードは[Github](https://github.com/t-hiroyoshi/akka-http-vs-spray)にあげました。

それぞれのバージョン等は以下の通りです。

```build.sbt
// sbt.version = 0.13.11
// addSbtPlugin("io.spray" % "sbt-revolver" % "0.8.0")
// addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.2")

lazy val commonSettings = Seq(
  name := "akka-http-vs-spray",
  version := "1.0.0",
  scalaVersion := "2.11.8",
  scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8", "-Xlint")
)

val akkaV = "2.4.4"
val sprayV = "1.3.3"

lazy val spraySettings = Seq(
  libraryDependencies ++= {
    Seq(
      "com.typesafe.akka"   %%  "akka-actor"    % akkaV,
      "io.spray"            %%  "spray-can"     % sprayV,
      "io.spray"            %%  "spray-routing" % sprayV
    )
  }
)

lazy val akkaSettings = Seq(
  libraryDependencies ++= {
    Seq(
      "com.typesafe.akka" %% "akka-actor" % akkaV,
      "com.typesafe.akka" %% "akka-stream" % akkaV,
      "com.typesafe.akka" %% "akka-http-experimental" % akkaV
    )
  }
)

lazy val sprayServer = (project in file("spray-server"))
.settings(commonSettings: _*)
.settings(spraySettings: _*)
.settings(assemblyOutputPath in assembly := file("./spray-server.jar"))

lazy val akkaServer = (project in file("akka-server"))
.settings(commonSettings: _*)
.settings(akkaSettings: _*)
.settings(assemblyOutputPath in assembly := file("./akka-server.jar"))

Revolver.settings
```

```Shell
java version "1.8.0_71"
Java(TM) SE Runtime Environment (build 1.8.0_71-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.71-b15, mixed mode)
```

`sbt assembly`したjarをローカルで`java -jar`してベンチマークをとりました。

ローカル環境

```Shell
MacBook Pro (Retina, 13-inch, Early 2015)
OS: OSX 10.11.4
Processor: 3.1 GHz Intel Core i7
Memory: 16 GB 1867 MHz DDR3
```

ベンチマークにはApacheBenchを用いました。

```Shell
ab -k -c 10 -n 10000 http://localhost:8080/
```

以下それぞれの結果です。

## Akka-HTTP

### Code

```Scala
package akka.server

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer

object Boot extends App {
  implicit lazy val system = ActorSystem("akka-server")
  implicit lazy val materializer = ActorMaterializer()
  implicit val ec = system.dispatcher

  private val route =
    pathSingleSlash {
      get {
        complete("Hello world!")
      }
    }

  Http().bindAndHandle(route, "localhost", 8080)
}
```

### Result

```Shell
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>

Server Software:        akka-http/2.4.4
Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      10
Time taken for tests:   0.465 seconds
Complete requests:      10000
Failed requests:        0
Keep-Alive requests:    10000
Total transferred:      1780000 bytes
HTML transferred:       120000 bytes
Requests per second:    21490.49 [#/sec] (mean)
Time per request:       0.465 [ms] (mean)
Time per request:       0.047 [ms] (mean, across all concurrent requests)
Transfer rate:          3735.65 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:     0    0   1.0      0      42
Waiting:        0    0   1.0      0      42
Total:          0    0   1.0      0      42

Percentage of the requests served within a certain time (ms)
  50%      0
  66%      0
  75%      0
  80%      0
  90%      1
  95%      1
  98%      2
  99%      3
 100%     42 (longest request)
```

## Spray-can

### Code

```Scala
package spray.server

import akka.actor._
import akka.io.IO
import spray.can.Http
import spray.routing._
import spray.http._
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._

object Boot extends App {
  implicit val system = ActorSystem("spray-server")
  implicit val timeout = Timeout(5.seconds)
  val service = system.actorOf(Props[ServiceActor], "spray-service")

  IO(Http) ? Http.Bind(service, "localhost", 8080)
}

class ServiceActor extends Actor with HttpService {
  private val route =
    pathSingleSlash {
      get {
        complete("Hello world!")
      }
    }

  def actorRefFactory = context
  def receive = runRoute(route)
}
```

### Result

```Shell
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>

Server Software:        spray-can/1.3.3
Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      10
Time taken for tests:   0.430 seconds
Complete requests:      10000
Failed requests:        0
Keep-Alive requests:    10000
Total transferred:      1780000 bytes
HTML transferred:       120000 bytes
Requests per second:    23230.85 [#/sec] (mean)
Time per request:       0.430 [ms] (mean)
Time per request:       0.043 [ms] (mean, across all concurrent requests)
Transfer rate:          4038.18 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:     0    0   0.9      0      20
Waiting:        0    0   0.9      0      20
Total:          0    0   0.9      0      20

Percentage of the requests served within a certain time (ms)
  50%      0
  66%      0
  75%      0
  80%      0
  90%      1
  95%      1
  98%      2
  99%      3
 100%     20 (longest request)
 ```

## まとめ

Akka-HTTPが21490.49 req/sec、Spray-canが23230.85 req/secと割と近い数値が出た気がします。
Akka-HTTPは前もっと使い物にならないくらい遅かったイメージだったんですがこれなら十分なリクエストがさばけそうです。