我正在尝试创建一个Akka系统,该系统会响应HTTP请求。我创建了一些交换信息的演员。我也可以使用akka-http来响应HTTP请求。问题在于连接这两个部分。
TL; DR:如何在akka-http请求处理期间与Akka演员交谈?
我创建了一个负责启动HTTP系统的角色:
class HttpActor extends Actor with ActorLogging {
/* implicits elided */
private def initHttp() = {
val route: Route = path("status") { get { complete { "OK" } } }
Http()(context.system).bindAndHandle(route, "localhost", 8080)
}
private var bound: Option[Future[Http.ServerBinding]] = None
override def receive = {
case HttpActor.Init =>
bound match {
case Some(x) => log.warning("Http already bootstrapping")
case None =>
bound = Some(initHttp(watcher))
}
}
}
object HttpActor {
case object Init
}
正如您所看到的,actor在它收到的第一条消息上创建了akka-http服务(没有理由,实际上,它也可以在构造函数中执行)。
现在,在请求处理期间,我需要与其他一些演员进行沟通,但我无法让它发挥作用。
我的方法:
private def initInteractiveHttp() = {
val route: Route = path("status") {
get { complete { "OK" } }
} ~ path("ask") {
get { complete {
// Here are the interesting two lines:
val otherActorResponse = someOtherActor ? SomeMessage
otherActorResponse.mapTo[String]
} }
Http()(context.system).bindAndHandle(route, "localhost", 8080)
}
这将向SomeMessage
发送someOtherActor
并在完成请求 - 响应周期之前等待响应。但是,据我所知,这些消息将从根HttpActor
发送,这很糟糕,在可扩展性方面无处可寻。理想情况下,我会为每个请求创建一个独特的专用actor实例,但由于akka-http输入而失败。请考虑以下示例:
class DisposableActor(httpContext: HttpContext) {
override def preStart() = {
// ... send some questions to other actors
}
override def receive = {
case GotAllData(x) => httpContext.complete(x)
}
}
class HttpActorWithDisposables {
// there is a `context` value in scope - we're an actor, after all
private def initHttpWithDisposableActors() = {
val route: Route = path("status") {
get { complete { "OK" } }
} ~ path("ask") {
get { httpContext =>
val props = Props(new DisposableActor(httpContext))
val disposableActor = context.actorOf(props, "disposable-actor-name")
// I have nothing to return here
}
}
Http()(context.system).bindAndHandle(route, "localhost", 8080)
}
通过这种方法,我可以强制DisposableActor在某个时间点调用httpContext.complete
。这应该正确结束请求 - 响应处理周期。但是,Route DSL需要在get
块内返回有效响应(或Future),因此这种方法不起作用。
答案 0 :(得分:4)
实际上,你的第一种方法很不错。
ask模式为您创建一个轻量级的一次性actor,它等待结果(非阻塞)以完成未来。它基本上完成了你想用DisposableActor
复制的所有东西,而你的主要HttpActor并没有因此而受到压力。
如果您仍想使用其他演员,则有completeWith
:
completeWith(instanceOf[String]) { complete =>
// complete is of type String => Unit
val props = Props(new DisposableActor(complete))
val disposableActor = context.actorOf(props, "disposable-actor-name")
// completeWith expects you to return unit
}
在你的演员中,当你得到结果时调用完整的函数
class DisposableActor(complete: String => Unit) {
override def receive = {
case GotAllData(x) =>
complete(x)
context stop self // don't for get to stop the actor
}
}