为什么我的请求由 spray-http 中的单个线程处理?

Why are my requests handled by a single thread in spray-http?

我使用 spray-can、spray-http 1.3.2 和 akka 2.3.6 设置了一个 http 服务器。 我的 application.conf 没有任何 akka(或喷雾)条目。我的演员代码:

class TestActor extends HttpServiceActor with ActorLogging with PlayJsonSupport {
  val route = get { 
    path("clientapi"/"orders") { 
       complete {{
            log.info("handling request")
            System.err.println("sleeping "+Thread.currentThread().getName)
            Thread.sleep(1000)
            System.err.println("woke up "+Thread.currentThread().getName)
            Seq[Int]()
       }}
    }
  }

  override def receive: Receive = runRoute(route)
}

开始是这样的:

val restService = system.actorOf(Props(classOf[TestActor]), "rest-clientapi")

IO(Http) ! Http.Bind(restService, serviceHost, servicePort)

当我发送 10 个并发请求时,它们都立即被 spray 接受并转发给不同的调度员角色(根据 akka 的日志记录配置,我已从 applicaiton.conf 中删除以免影响结果),但所有由同一个线程处理,该线程处于休眠状态,只有在醒来后才会接收下一个请求。

我应该add/change配置什么?从我在 reference.conf 中看到的情况来看,默认执行程序是一个 fork-join-executor,所以我希望所有请求都可以开箱即用地并行执行。

从你的代码中我看到只有一个 TestActor 来处理所有请求,因为你只创建了一个 system.actorOf。您知道,actorOf 不会根据请求创建新的演员 - 不仅如此,您在那里有 val,所以它只有一个演员。这个 actor 一个接一个地处理请求,你的路由在这个 actor 内部处理。调度程序没有理由选择另一个线程,而每次只有一个线程只被一个演员使用,所以你在日志中只有一个线程(但不能保证) - 我假设它是第一个池中的线程。

Fork-join 执行器除了提供第一个且始终相同的空闲线程外什么都不做,因为没有更多的参与者需要与当前线程并行的线程。因此,它一次只接收一项任务。即使使用 "work stealing" - 它也不会工作,直到你有一些阻塞(并标记为已管理块)线程到 "steal" 资源。 Thread.sleep(1000) 本身不会自动标记线程 - 您应该用 scala.concurrent.blocking 将其包围以使用 "work stealing"。不管怎样,只有一个演员,还是只有一个线程。

如果您需要多个参与者来处理请求 - 只需传递一些 akka router actor(它与 spray-router 没有任何共同点):

val restService = context.actorOf(RoundRobinPool(5).props(Props[TestActor]), "router")  

这将创建一个包含 5 个参与者的池(不是线程池)来满足您的请求。