spray.routing.HttpService如何调度请求? [英] How does spray.routing.HttpService dispatch requests?

查看:71
本文介绍了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. 我在示例中看到了单个演员的创建。

  2. 如果我在 complete 内部阻止调用会发生什么情况,是否意味着该应用程序是单线程的并且仅使用一个cpu内核? ?

  3. 如果是p。 1为真,p。 2将阻止,如何调度请求以利用所有cpus?我看到两种方式:每个请求的actor和每个连接的actor。第二个似乎是合理的,但是我找不到使用Spray库的方法。

  4. 如果上一个问题无关紧要,将分离指令吗?然后传递将Future返回到 complete 指令的函数呢?

  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上下文无关紧要。重要的是,它
确实在配置的请求超时时间段内发生。您可以通过某种方式自己调用
课程,但是Spray
可以为您提供许多预定义的结构,这些结构可能比传递实际的RequestContext更好地适合您的
体系结构。
主要是:

In the end the only thing that really completes the request is a call to requestContext.complete. 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:


  1. complete 指令,该指令仅提供在 raw ctx =>顶部放一些糖ctx.complete(…)函数文字。

  2. 未来的编组器,它从中调用 ctx.complete future.onComplete 处理程序。

  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
层泄漏到应用程序的核心中。即应用程序
应该不了解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.

重新排序以向并依赖Future马歇尔(Marshaller)是显而易见的,容易理解的,相当容易的替代方案。它具有
的(小)缺点,即询问本身带有强制性的超时检查
,这在逻辑上是不需要的(因为喷雾罐层
已经处理了请求超时) 。由于技术原因,要求的超时时间是
(因此,如果期望的答复从未到来,则可以清除基础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]){completeer =>
)。它提取一个可以传递给应用程序
核心的函数。当您的核心逻辑调用 complete(foo)时,运行完成逻辑
并完成请求。因此,应用程序核心保持与API层分离的
,并且开销很小。这种方法的
缺点有两个:首先,完成函数
不可序列化,因此您不能跨JVM
边界使用此方法。其次,完成逻辑现在直接在应用程序核心的actor上下文中运行
,如果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层
中生成每个请求的参与者,并使其处理来自应用程序核心的响应。
然后,您不必使用询问。仍然,您仍然遇到一个与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.

要回答您的一些特定问题:只有一个角色/处理者为路线提供服务,因此如果您将其阻挡,Spray将会阻挡。这意味着您要立即完成路由,或者要使用上面的三个选项之一来调度工作。

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.

网络上有很多关于这三个选项的示例。最简单的方法是将代码包装在 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配置仍然适用。请参阅HOCON reference.conf application.conf 了解Actor线程设置。

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如何调度请求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆