简体   繁体   English

如何在Spray.routing中同时安全地处理路由请求?

[英]How can I safely process routing requests concurrently in spray.routing?

It seems that the example uses of the Spray HTTP server make it painfully easy to make the server process requests sequentially instead of concurrently. 看来,使用Spray HTTP服务器的示例使通过顺序而不是同时进行服务器处理请求变得非常容易。 This is true because the examples show the routing object implemented as an actor that processes one request at a time (facepalm? ** ). 这是正确的,因为这些示例显示了路由对象被实现为一次处理一个请求的actor(facepalm?**)。 This seems to be a common problem . 似乎 是一个普遍的问题

For example, below, accessing /work1 processes the request asynchronously, but for /work2 we unfortunately block ALL other request (let's assume, for example that /work2 needs to get busy authenticating a token from the cookie in a database). 例如,在下面,访问/ work1异步处理该请求,但是不幸的是,对于/ work2,我们阻止了所有其他请求(例如,假设/ work2需要忙于从数据库中的cookie验证令牌)。

Is there a way use spray.routing where execution is forked before getting to the routing? 有没有一种方法可以使用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"
              )
            }
          } 
      }
    }
  }
}

** since routing is typically a stateless operation there doesn't seems to be a benefit to making the router and Actor, right? **由于路由通常是无状态操作,因此制造路由器和Actor似乎没有好处,对吗?

For every other webserver I've used, forking control of the connection to a handler process or thread more sensibly (IMO) happens almost immediately after accepting a TCP connection. 对于我使用过的所有其他Web服务器,接受TCP连接后,几乎立即就可以分叉控制与处理程序进程或线程(IMO)的连接。 (I think) this maximizes the rate at which connections can be received and minimizes the risk on unintentional blocking -- at least, completely avoids unintentional blocking in routing. (我认为)这可以最大程度地提高接收连接的速率,并最大程度地减少意外阻塞的风险-至少可以完全避免路由中的意外阻塞。


Update: 更新:

As @rahilb suggested 正如@rahilb建议的

  detach() {
    get {...} ..
 }

and calling as: 并致电为:

 val responses = (0 until 10)
    .map { _ => (IO(Http) ? HttpRequest(GET, s"${TEST_BASE_URL}work1")).mapTo[HttpResponse] }
    .map { response => Await.result(response, 5 seconds) }

... still takes around >3 seconds for either work1 or work2. ... work1或work2仍然需要大约3秒以上的时间。

Actually even your work2 route has the potential to starve the HTTP Actor, as the ExecutionContext used in Future.apply is system.dispatcher , ie the spray HttpServiceActor 's context. 其实即使你work2路线有饿死的HTTP演员,作为潜在的ExecutionContext中使用Future.applysystem.dispatcher ,即喷雾HttpServiceActor的上下文。 We can provide a different ExecutionContext for long running futures so we do not risk starving spray's one. 我们可以为长期期货提供不同的ExecutionContext,因此我们不会冒着饿死喷雾的风险。

To answer your question though, there is a directive called detach that will run the rest of the route in some ExecutionContext, potentially leaving more resources free to incoming requests... but as it is a directive forking only occurs after the route is hit. 不过,要回答您的问题,有一个名为detach的指令将在某些ExecutionContext中运行路由的其余部分,从而有可能为传入的请求留下更多的资源...但这是一个指令分叉,仅在路由被点击后才会发生。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM