スプレー HTTP サーバーの使用例を見ると、サーバーがリクエストを同時に処理するのではなく、順番に処理することが非常に簡単になっているようです。これは、例が一度に 1 つの要求を処理するアクターとして実装されたルーティング オブジェクトを示しているためです (facepalm? ** )。これはよくある問題のようです。
たとえば、以下では、/work1 にアクセスすると要求が非同期に処理されますが、/work2 については残念ながら他のすべての要求がブロックされます (たとえば、/work2 がデータベース内の Cookie からトークンを認証するためにビジー状態になる必要があるとします)。
ルーティングに到達する前に実行が分岐する場所で、spray.routing を使用する方法はありますか?
import akka.actor.ActorSystem
import spray.http.{MediaTypes, HttpEntity}
import spray.routing.SimpleRoutingApp
import scala.concurrent.Future
class MySimpleServer(val system: ActorSystem, val HOST: String, val PORT: Int) extends SimpleRoutingApp {
implicit val _system: ActorSystem = system
import _system.dispatcher
def main(args: Array[String]): Unit = {
startServer(interface = HOST, port = PORT) {
get {
path("work1") {
complete {
// Asynchronously process some work
Future.apply {
Thread.sleep(1000)
HttpEntity(
MediaTypes.`text/html`,
"OK"
)
}
}
} ~
path("work2") {
complete {
// Synchronously process some work and block all routing for this Actor.
// Oh sh*t!
Thread.sleep(1000)
HttpEntity(
MediaTypes.`text/html`,
"OK"
)
}
}
}
}
}
}
** 通常、ルーティングはステートレスな操作であるため、ルーターとアクタを作成してもメリットはないようです。
私が使用した他のすべてのWebサーバーでは、TCP接続を受け入れた直後に、ハンドラープロセスまたはスレッドへの接続の分岐制御がより賢明に(IMO)発生します。(私が思うに)これにより、接続を受信できる速度が最大になり、意図しないブロックのリスクが最小限に抑えられます。少なくとも、ルーティングでの意図しないブロックが完全に回避されます。
アップデート:
@rahilbが提案したように
detach() {
get {...} ..
}
そして次のように呼び出します:
val responses = (0 until 10)
.map { _ => (IO(Http) ? HttpRequest(GET, s"${TEST_BASE_URL}work1")).mapTo[HttpResponse] }
.map { response => Await.result(response, 5 seconds) }
... work1 または work2 のいずれかで、まだ約 3 秒かかります。