免责声明:我目前没有 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.
如果你能在官方文档中指出我的解释就好了.它非常广泛,我相信我遗漏了一些东西.
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:
最后真正完成请求的只有一个电话requestContextplete.因此哪个线程并不重要或此调用的 Actor 上下文.重要的是它确实发生在配置的请求超时"期间.你可以当然以某种方式向自己发出这个呼叫,但是喷为您提供了许多可能适合您的预定义结构架构比传递实际的 RequestContext 更好.主要有:
In the end the only thing that really completes the request is a call to requestContextplete. Thereby it doesn't matter which thread or Actor context this call is made from. All that matters is that it does happen within the configured "request-timeout" period. You can of course issue this call yourself in some way or another, but spray gives you a number of pre-defined constructs that maybe fit your architecture better than passing the actual RequestContext around. Mainly these are:
从架构上来说,在大多数情况下,没有 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.conf 和 application.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 如何调度请求?
发布评论