本文介绍了Spray.routing.HttpService 如何调度请求?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

免责声明:我目前没有 Scala 经验,所以我的问题与非常基础有关.

Disclaimer: I have no scala experience for now, so my question is connected with very basics.

考虑以下示例(可能不完整):

Consider the following example (it may be incomplete):

import akka.actor.{ActorSystem, Props}
import akka.io.IO
import spray.can.Http
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._
import akka.actor.Actor
import spray.routing._
import spray.http._

object Boot extends App {
  implicit val system = ActorSystem("my-actor-system")
  val service = system.actorOf(Props[MyActor], "my")
  implicit val timeout = Timeout(5.seconds)
  IO(Http) ? Http.Bind(service, interface = "localhost", port = 8080)
}

class MyActor extends Actor with MyService {
  def actorRefFactory = context

  def receive = runRoute(myRoute)
}

trait MyService extends HttpService {
  val myRoute =
    path("my") {
      post {
        complete {
          "PONG"
        }
      }
    }
}

我的问题是:当控制到达 complete 块时实际会发生什么?这个问题好像太笼统了,我分一下吧.

My question is: what actually happens when control reaches complete block? The question seems to be too general, so let me split it.

  1. 我在示例中看到了单个演员的创建.是不是说应用是单线程的,只用了一个cpu核?
  2. 如果我在 complete 中阻塞调用会怎样?
  3. 如果第1 为真,p.2 会阻塞,我如何调度请求以利用所有 CPU?我看到两种方式:每个请求的参与者和每个连接的参与者.第二个似乎合理,但我找不到使用spray库的方法.
  4. 如果前面的问题无关紧要,detach 指令会做吗?将返回 Future 的函数传递给 complete 指令怎么样?分离和传递函数返回 Future 有什么区别?
  5. 配置工作线程数和平衡请求/连接的正确方法是什么?
  1. I see creation of a single actor in the example. Does it mean that the application is single-threaded and uses only one cpu core?
  2. What happens if I do blocking call inside complete?
  3. If p. 1 is true and p. 2 will block, how do I dispatch requests to utilize all cpus? I see two ways: actor per request and actor per connection. The second one seems to be reasonable, but I cannot find the way to do it using spray library.
  4. If the previous question is irrelevant, will detach directive do? And what about passing function returning Future to complete directive? What is the difference between detach and passing function returning the Future?
  5. What is the proper way to configure number of working threads and balance requests/connections?

如果你能在官方文档中指出我的解释就好了.它非常广泛,我相信我遗漏了一些东西.

It would be great if you point me explanations in the official documentation. It is very extensive and I believe I am missing something.

谢谢.

推荐答案

已回答 此处 作者 Mathias - Spray 作者之一.复制他的回复以供参考:

It's answered here by Mathias - one of the Spray authors. Copying his reply for the reference:

最后真正完成请求的只有一个电话requestContext.complete.因此哪个线程并不重要或此调用的 Actor 上下文.重要的是它确实发生在配置的请求超时"期间.你可以当然以某种方式向自己发出这个呼叫,但是喷为您提供了许多可能适合您的预定义结构架构比传递实际的 RequestContext 更好.主要有:

  1. complete 指令,它只是在原始"ctx => 之上提供一些糖;ctx.complete(…) 函数字面量.
  2. Future Marshaller,它从 future.onComplete 处理程序调用 ctx.complete.
  3. produce 指令,它提取一个函数 T =>单元,稍后可用于通过自定义实例完成请求类型.
  1. The complete directive, which simply provides some sugar on top of the "raw" ctx => ctx.complete(…) function literal.
  2. The Future Marshaller, which calls ctx.complete from an future.onComplete handler.
  3. The produce directive, which extracts a function T => Unit that can later be used to complete the request with an instance of a custom type.

从架构上来说,在大多数情况下,没有 API 是个好主意层渗入"应用程序的核心.IE.应用程序应该对 API 层或 HTTP 一无所知.它应该只处理自己领域模型的对象.因此通过RequestContext 直接到应用程序核心大多不是最好的解决方案.

Architecturally, in most cases, it's a good idea to not have the API layer "leak into" the core of your application. I.e. the application should not know anything about the API layer or HTTP. It should only deal with objects of its own domain model. Therefore passing the RequestContext directly to the application core is mostly not the best solution.

诉诸问",依靠未来的马歇尔是一种显而易见,易于理解且相当简单的替代方案.它带有问带有强制性超时检查的(小)缺点逻辑上不需要的本身(因为喷雾罐层已经处理请求超时).问的超时时间是出于技术原因需要(因此底层的 PromiseActorRef 可以如果预期的回复永远不会出现,则清理).

Resorting to the "ask" and relying on the Future Marshaller is an obvious, well understood and rather easy alternative. It comes with the (small) drawback that an ask comes with a mandatory timeout check itself which logically isn't required (since the spray-can layer already takes care of request timeouts). The timeout on the ask is required for technical reasons (so the underlying PromiseActorRef can be cleaned up if the expected reply never comes).

另一种传递 RequestContext 的替代方法是produce 指令(例如 produce(instanceOf[Foo]) { completer =>...).它提取了一个可以传递给应用程序的函数核.当你的核心逻辑调用 complete(foo) 完成逻辑运行并且请求完成.从而应用核心保留与 API 层解耦,开销极小.这这种方法的缺点是双重的:首先是 completer 函数不可序列化,因此您不能在 JVM 中使用这种方法边界.其次完成逻辑现在直接运行在应用程序核心的参与者上下文中,这可能会改变如果 Marshaller[Foo] 必须这样做,则以不需要的方式运行时行为非平凡的任务.

Another alternative to passing the RequestContext around is the produce directive (e.g. produce(instanceOf[Foo]) { completer => …). It extracts a function that you can pass on to the application core. When your core logic calls complete(foo) the completion logic is run and the request completed. Thereby the application core remains decoupled from the API layer and the overhead is minimal. The drawbacks of this approach are twofold: first the completer function is not serializable, so you cannot use this approach across JVM boundaries. And secondly the completion logic is now running directly in an actor context of the application core, which might change runtime behavior in unwanted ways if the Marshaller[Foo] has to do non-trivial tasks.

第三种选择是在 API 层生成每个请求的 actor并让它处理从应用程序核心返回的响应.那么你不必使用询问.尽管如此,你最终还是一样问题背后的 PromiseActorRef 存在的问题:如何清理如果没有响应从应用程序核心返回?用一个重新请求参与者,您有充分的自由来为其实施解决方案这个问题.但是,如果您决定依赖超时(例如通过context.setReceiveTimeout) 比询问"的好处可能是不存在的.

A third alternative is to spawn a per-request actor in the API layer and have it handle the response coming back from the application core. Then you do not have to use an ask. Still, you end up with the same problem that the PromiseActorRef underlying an ask has: how to clean up if no response ever comes back from the application core? With a re-request actor you have full freedom to implement a solution for this question. However, if you decide to rely on a timeout (e.g. via context.setReceiveTimeout) the benefits over an "ask" might be non-existent.

所描述的哪种解决方案最适合您需要的架构自己决定.然而,正如我希望能够展示的那样,你确实做到了有多种选择可供选择.

Which of the described solutions best fits you architecture you need to decide yourself. However, as I hopefully was able to show, you do have a couple of alternatives to choose from.

回答你的一些具体问题:只有一个actor/handler为路由提供服务,因此如果你让它阻塞,Spray就会阻塞.这意味着您要立即完成路线或使用上述 3 个选项之一调度工作.

To answer some of your specific questions: There is only a single actor/handler that services the route thus if you make it block Spray will block. This means you want to either complete the route immediately or dispatch work using either of the 3 options above.

网络上有很多关于这 3 个选项的示例.最简单的方法是将您的代码包装在 Future 中.还检查每个请求的演员"选项/示例.最后,您的架构将定义最合适的方式.

There are many examples on the web for these 3 options. The easiest is to wrap your code in a Future. Check also "actor per request" option/example. In the end your architecture will define the most appropriate way to go.

最后,Spray 运行在 Akka 之上,因此所有 Akka 配置仍然适用.有关 Actor 线程设置的信息,请参阅 HOCON reference.confapplication.conf.

Finally, Spray runs on top of Akka, so all Akka configuration still applies. See HOCON reference.conf and application.conf for Actor threading settings.

这篇关于Spray.routing.HttpService 如何调度请求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-28 20:01