akka-http查询不能并行运行

时间:2016-11-10 13:50:27

标签: scala parallel-processing akka akka-http

我是akka-http的新手,并且在同一路径上并行运行查询时遇到麻烦。

我有一个可以非常快速地返回结果的路由(如果缓存)或者没有(繁重的CPU多线程计算)。我想并行运行这些查询,如果一个较短的查询在经过长时间的计算之后到达,我不希望第二次调用等待第一次完成。

但是,如果这些查询位于同一路径上并且并行运行(如果在不同的路由上并行运行),则这些查询似乎不会并行运行。

我可以在一个基本项目中重现它:

并行调用服务器3次(http://localhost:8080/test上有3个Chrome标签页)会导致响应分别达到3.0秒,6.0秒和9.0秒。我认为查询不会并行运行。

使用jdk 8在Windows 10上运行6核(带HT)计算机。

build.sbt

name := "akka-http-test"

version := "1.0"

scalaVersion := "2.11.8"

libraryDependencies += "com.typesafe.akka" %% "akka-http-experimental" % "2.4.11"

* AkkaHttpTest.scala **

import java.util.concurrent.Executors

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

import scala.concurrent.{ExecutionContext, Future}

object AkkaHttpTest extends App {

  implicit val actorSystem = ActorSystem("system") // no application.conf here
  implicit val executionContext = 
                             ExecutionContext.fromExecutor(Executors.newFixedThreadPool(6))
  implicit val actorMaterializer = ActorMaterializer()

  val route = path("test") {
    onComplete(slowFunc()) { slowFuncResult =>
      complete(slowFuncResult)
    }
  }

  def slowFunc()(implicit ec: ExecutionContext): Future[String] = Future {
    Thread.sleep(3000)
    "Waited 3s"
  }

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

  println("server started")
}

我在这里做错了什么?

感谢您的帮助

编辑:感谢@Ramon J Romero y Vigil,我添加了Future Wrapping,但问题仍然存在

def slowFunc()(implicit ec : ExecutionContext) : Future[String] = Future {
  Thread.sleep(3000)
  "Waited 3.0s"
}

val route = path("test") {
  onComplete(slowFunc()) { slowFuncResult =>
    complete(slowFuncResult)
  }
}

尝试使用默认的线程池,配置文件中定义的线程池和固定线程池(6个线程)。

似乎onComplete指令仍然等待将来完成然后阻塞Route(使用相同的连接)。

Flow技巧的相同问题

import akka.stream.scaladsl.Flow

val parallelism = 10

val reqFlow = 
  Flow[HttpRequest].filter(_.getUri().path().equalsIgnoreCase("/test"))
                   .mapAsync(parallelism)(_ => slowFunc())
                   .map(str => HttpResponse(status=StatusCodes.Ok, entity=str))

Http().bindAndHandle(reqFlow, ...)

感谢您的帮助

2 个答案:

答案 0 :(得分:5)

每个IncomingConnection由同一个路由处理,因此当您"并行呼叫服务器3次时#34;您可能使用相同的连接,因此使用相同的路由。

Route以akka-stream方式处理所有3个传入的HttpRequest值,即Route由多个阶段组成,但每个阶段在任何给定时间只能处理1个元素。在你的例子中,"完成"流的阶段将为每个传入的请求调用Thread.sleep并一次处理每个请求。

要同时处理多个并发请求,您应为每个请求建立唯一的连接。

可以创建客户端连接池的示例similar to the documentation examples

import akka.http.scaladsl.Http

val connPoolFlow = Http().newHostConnectionPool("localhost", 8080)

然后可以将其集成到发出请求的流中:

import akka.http.scaladsl.model.Uri._
import akka.http.scaladsl.model.HttpRequest

val request = HttpRequest(uri="/test")

import akka.stream.scaladsl.Source

val reqStream = 
  Source.fromIterator(() => Iterator.continually(request).take(3))
        .via(connPoolFlow)
        .via(Flow.mapAsync(3)(identity))
        .to(Sink foreach { resp => println(resp)})
        .run()

路线修改

如果您希望每个HttpRequest并行处理,那么您可以使用相同的Route来执行此操作,但您必须在Route内部生成Futures并使用onComplete指令:

def slowFunc()(implicit ec : ExecutionContext) : Future[String] = Future {
  Thread.sleep(1500)
  "Waited 1.5s"
}

val route = path("test") {
  onComplete(slowFunc()) { slowFuncResult =>
    complete(slowFuncResult)
  }
}

要注意的一件事是:如果您没有为睡眠功能指定不同的ExecutionContext,那么路由的相同线程池将用于您的睡眠。您可以通过这种方式耗尽可用的线程。你可能应该使用单独的ec来睡觉...

基于流量

处理HttpRequests的另一种方法是使用a stream Flow

import akka.stream.scaladsl.Flow

val parallelism = 10

val reqFlow = 
  Flow[HttpRequest].filter(_.getUri().path().equalsIgnoreCase("/test"))
                   .mapAsync(parallelism)(_ => slowFunc())
                   .map(str => HttpResponse(status=StatusCodes.Ok, entity=str))

Http().bindAndHandle(reqFlow, ...)

答案 1 :(得分:1)

如果这仍然有意义,或者对于将来的读者,答案在Http().bindAndHandle文档中:

 /**
   * Convenience method which starts a new HTTP server...
   * ... 
   * The number of concurrently accepted connections can be configured by overriding
   * the `akka.http.server.max-connections` setting....
   * ...
   */
  def bindAndHandle(...

使用akka.http.server.max-connections设置并发连接数。