Asynchronous 将core.async与阻塞客户机/驱动程序一起使用:有性能优势吗?

Asynchronous 将core.async与阻塞客户机/驱动程序一起使用:有性能优势吗?,asynchronous,clojure,blocking,core.async,Asynchronous,Clojure,Blocking,Core.async,我正在Clojure中编程一个web应用程序后端,其中包括: 作为HTTP服务器和客户端(非阻塞) 作为我的数据库驱动程序(阻塞) 作为S3客户端(阻塞) 我知道事件驱动、非阻塞堆栈(如NodeJS和Play框架上的堆栈)的性能优势(帮助了我),以及它如何产生更好的负载容量。出于这个原因,我正在考虑使用core.async使后端异步 我的问题是:在阻塞客户机/驱动程序库的基础上使用core.async,是否可以重新创建非阻塞web堆栈的性能优势? 详细说明: 我目前正在做的是通常的同步调用

我正在Clojure中编程一个web应用程序后端,其中包括:

  • 作为HTTP服务器和客户端(非阻塞)
  • 作为我的数据库驱动程序(阻塞)
  • 作为S3客户端(阻塞)
我知道事件驱动、非阻塞堆栈(如NodeJS和Play框架上的堆栈)的性能优势(帮助了我),以及它如何产生更好的负载容量。出于这个原因,我正在考虑使用core.async使后端异步

我的问题是:在阻塞客户机/驱动程序库的基础上使用core.async,是否可以重新创建非阻塞web堆栈的性能优势?


详细说明:

我目前正在做的是通常的同步调用:

(defn handle-my-request [req]
  (let [data1 (db/findData1)
        data2 (db/findData2)
        data3 (s3/findData3)
        result (make-something-of data1 data2 data3)]
    (ring.util.response/response result))
  )
我计划做的是将涉及IO的任何调用包装到块中,并在块中同步该调用

(defn处理我的请求![req resp chan];;resp chan是一个core.async通道,必须通过它推送响应
(去
(让[data1ch(thread(db/findData1));;旋转线程以获取数据(涉及IO)
数据2通道(线程(db/findData2))
数据3通道(线程(s3/findData3))
结果(利用(>(ring.util.response/response result)
(>!resp chan));发送响应
)))
这样做有意义吗?

我这样做是因为这是我发现的最佳实践,但它们的性能优势对我来说仍然是个谜。我认为同步堆栈的问题在于,它们每个请求使用一个线程。现在,它们似乎使用了多个线程


提前感谢您的帮助,祝您度过美好的一天。

您的示例的好处是findData1、2和3是并行运行的,这可以以使用更多线程为代价减少响应时间


根据我的经验,通常情况下,对findData2的调用取决于findData1的结果,而findData3则取决于findData2的结果,这意味着调用无法并行化,在这种情况下,使用core.async是没有意义的。简单的答案是不,您不会以这种方式增加容量。如果你的内存可以容纳100个线程,那么你就有300“线程秒”每3秒间隔的容量。因此,假设每个块的执行时间为1秒。无论每个请求是同步运行,保持线程完整3秒,还是异步阻塞,保持线程1秒3次,每3秒的请求数都不会超过100个

然而,如果您将一个步骤设置为异步,那么突然之间,您的代码每个请求只需要两个线程秒,因此您现在可以每三秒处理300/2=150个请求

更复杂的答案是,它可能会使情况变得更好或更糟,这取决于您的客户端或web服务器如何处理超时、客户端重试请求的速度/频率、代码的可并行性、线程交换的成本等等。如果您尝试在同步实现中执行200个请求,那么100个请求将在3秒后通过在异步实现中,由于它们都在不同的异步连接点上竞争线程,所以大多数线程需要5-6秒才能完成。就是这样。但是如果这些块是可并行的,那么一些请求可能在一秒钟内完成,也就是这样


因此,这取决于边缘,但最终的容量是线程秒数,而标准的同步或块异步都是一样的。它不是Clojure特定的,而且肯定有比我在这里提供的更多更深入的资源来详细说明所有边缘情况。

谢谢,还有潜在的问题吗负载能力优势?是否因为驾驶员阻塞而受损?这是正确的。负载能力不会降低improve@ValentinWaeselynck启动新线程的开销实际上会使情况变得更糟。您必须运行一些负载测试来确定更糟的程度。顺便说一句,除非您运行clojure,否则我猜不会太糟-clr。这些线程来自线程池,因此创建它们的成本可以忽略,除非您有一个非常尖利的负载。另一方面,如果我有用于其他操作的非阻塞库(例如,我有一个真正的非阻塞HTTP客户端和服务器,如HTTP工具包),那么我就不会为这些操作阻塞任何线程;这样,每个线程的阻塞时间就减少了,所以这仍然是一个改进。这样做的一个小小好处是,当/如果异步驱动程序可用时,集成将相当无缝(假设API遵循约定)。在dAni的答案发布后,你打开了“赏金”,它似乎完全回答了这个问题。你还不确定什么?dAni的答案更多的是关于处理一个单独请求的速度;我的问题更多的是关于总体负载能力。谢谢,这是我需要的答案。如果你能给我提供参考,我将非常感兴趣se资源。
(defn handle-my-request! [req resp-chan] ;; resp-chan is a core.async channel through which the response must be pushed
  (go 
    (let [data1-ch (thread (db/findData1)) ;; spin of threads to fetch the data (involves IO)
          data2-ch (thread (db/findData2))
          data3-ch (thread (s3/findData3))
          result (make-something-of (<! data1-ch) (<! data2-ch) (<! data3-ch))] ;; synchronize
     (->> (ring.util.response/response result)
       (>! resp-chan)) ;; send response
     )))