当前位置: 首页 > 知识库问答 >
问题:

我可以用http工具包和核心制作一个完全无阻塞的后端应用程序吗。异步的?

杨海
2023-03-14

我想知道是否有可能用http工具包组装一个完全无阻塞的Clojure后端web应用程序。

(实际上,我认为任何与环兼容的http服务器都可以;我之所以提到http工具包,是因为它声称有一个事件驱动的非阻塞模型)。

这个问题是我对非阻塞/异步/事件驱动系统本质的一些误解的症状。如果你和我在同一个地方,这里有一些澄清。

只有当IO的所有(比如说大部分)都以非阻塞的方式从头开始处理时,才能使事件驱动系统具有非阻塞(如Node.js)的性能优势。这意味着您的所有DB驱动程序、HTTP服务器和客户端、Web服务等首先必须提供异步接口。特别地:

  • 如果您的数据库驱动程序提供了同步接口,则无法使其非阻塞。(你的线程被阻塞了,没有办法找回它)。如果你想要无阻塞,你需要使用其他东西。
  • 像core.async这样的高级协调实用程序不能使系统非阻塞。它们可以帮助您管理非阻塞代码,但不要启用它。
  • 如果您的IO驱动程序是同步的,您可以使用core.async来获得异步的设计优势,但您不会获得异步的性能优势。您的线程仍然会浪费时间等待每个响应。

现在,具体来说:

  • http工具包作为http服务器提供了一个非阻塞的异步接口。见下文
  • 然而,许多环中间件,因为它们本质上是同步的,将与这种方法不兼容。基本上,任何更新返回响应的环中间件都不可用

如果我做对了(我不是专家,所以请告诉我我是否在错误的假设下工作),这种web应用程序的非阻塞模型的原则如下:

  1. 让几个超快的操作系统线程处理所有CPU密集型计算;这些绝对不能等待。
  2. 有很多弱线程处理IO(数据库调用、Web服务调用、Hibernate等);这些主要是等待。
  3. 这是有益的,因为用于处理请求的等待时间通常比计算时间高2到5个数量级。

从我所看到的来看,这个模型在播放框架(Scala)和Node.js(JavaScript)平台上默认支持,并使用基于promise的实用程序以编程方式管理异步。

让我们尝试在基于环的clojure应用程序中使用Compojure路由实现这一点。我有一个路由,它通过调用my handle函数来构造响应:

(defroutes my-routes
  (GET "/my/url" req (my-handle req))
  )
(def my-app (noir.util.middleware/app-handler [my-routes]))
(defn start-my-server! [] 
  (http-kit/run-server my-app))

Clojure应用程序中管理异步的普遍接受的方式似乎是基于CSP的,并使用核心。异步库,我完全可以使用它。因此,如果我想接受上面列出的非阻塞原则,我会通过以下方式实现my handle

(require '[clojure.core.async :as a])

(defn my-handle [req]
  (a/<!!
    (a/go ; `go` makes channels calls asynchronous, so I'm not really waiting here
     (let [my-db-resource (a/thread (fetch-my-db-resource)) ; `thread` will delegate the waiting to "weaker" threads
           my-web-resource (a/thread (fetch-my-web-resource))]
       (construct-my-response (a/<! my-db-resource)
                              (a/<! my-web-resource)))
     )))

CPU密集型的构建我的响应任务在go块中执行,而等待外部资源则在线程块中完成,正如Tim Baldridge在本core视频中所建议的那样。异步(38'55'')

但这不足以使我的应用程序不阻塞。无论哪个线程通过我的路由,并将调用my-handly函数,都将等待构建响应,对吗?

让这个HTTP处理也非阻塞会有好处吗(我相信),如果是的话,我如何实现它?

编辑

正如codemomentum所指出的,对请求的非阻塞处理缺少的成分是使用超文本传输协议-kit通道。结合core.async,上面的代码会变成这样:

(defn my-handle! [req]
  (http-kit/with-channel req channel
    (a/go 
     (let [my-db-resource (a/thread (fetch-my-db-resource))
           my-web-resource (a/thread (fetch-my-web-resource))
           response (construct-my-response (a/<! my-db-resource)
                                           (a/<! my-web-resource))]
       (send! channel response)
       (close channel))
     )))

这确实让您接受了异步模型。

问题是它与环形中间件非常不兼容。环形中间件使用函数调用来获取响应,这使得响应基本上是同步的。更一般地说,事件驱动处理似乎与纯函数式编程接口不兼容,因为触发事件意味着有副作用。

我很高兴知道是否有一个Clojure库来解决这个问题。

共有1个答案

司空均
2023-03-14

使用异步方法,您可以在数据准备就绪时将数据发送到客户机,而不是在整个线程准备过程中阻塞它。

对于超文本传输协议-kit,您应该使用留档中描述的异步处理程序。以适当的方式将请求委托给异步处理程序后,您可以使用core.async或其他方法实现它。

异步处理程序留档在这里:http://http-kit.org/server.html#channel

 类似资料:
  • 我在读https://dzone.com/articles/think-twice-using-java-8

  • 我理解在反应流中使用阻塞操作时,我们应该使用

  • 我有一个位于服务器上的bash脚本和一个将在该服务器上运行的Java应用程序。我的目标是从Java应用程序调用这个脚本两次,以便两者同时运行。 我有以下代码: 这应该通过bash调用脚本,在后台运行它,然后在第一个完成之前立即启动另一个脚本(脚本运行大约需要十秒钟)。这一切似乎都工作得很好。 问题是,然后我想等到两个后台进程都完成后再转到Java程序的下一行。我尝试了这个: 然而,“下一行代码”似

  • 我可以用这样的代码在市场上发布我的应用程序吗 我阅读那个严格模式只是为了调试,当我发布应用程序时,我必须删除它。

  • 一般来说,我可以做到这一点,一切看起来都一样, 但也许以后我不能把我的应用程序上传到谷歌开发者控制台? 或者我会在实现谷歌libs方面遇到问题?

  • 问题内容: 我正在尝试使用在使用Python自动完成无聊的东西的第3章末尾找到的项目中的准则编写Collat​​z程序。我正在使用python 。以下是项目概述: 编写一个名为的函数,该函数具有一个名为number的参数。如果数字是偶数,则应打印并返回该值。如果数字为奇数,则应打印并返回。然后编写一个程序,让用户键入一个整数,并不断调用该数字,直到函数返回该值为止。 该程序的输出可能类似于以下内容