使用core.async阻止客户端/驱动程序:是否有性能优势?

时间:2014-10-19 18:40:10

标签: asynchronous clojure blocking core.async

我正在使用Clojure编写Web应用程序后端,其中包括:

  • http-kit作为HTTP服务器和客户端(非阻塞)
  • monger作为我的数据库驱动程序(阻止)
  • clj-aws-s3作为S3客户端(阻止)

我知道事件驱动的非阻塞堆栈的性能优势,例如你在NodeJS和Play Framework上找到的堆栈(this question帮助我),以及它如何产生更好的负载能力。出于这个原因,我正在考虑使用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的任何调用包装在thread块中,并在go块内同步,

(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
     )))

这样做有什么意义吗?

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

在此先感谢您的帮助,祝您度过美好的一天。

2 个答案:

答案 0 :(得分:2)

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

根据我的经验,通常会发生的事情是对findData2的调用取决于findData1的结果,而findData3依赖于findData2的结果,这意味着调用无法并行化,在这种情况下使用没有意义core.async

答案 1 :(得分:1)

简单的答案是否定的,你不会以这种方式增加容量。如果你有内存可以容纳100个线程,那么你已经有300&#34;线程秒数&#34;每3秒间隔的容量。所以,假设每个块需要一秒钟才能执行。如果每个请求同步运行,保持线程整整三秒,或者阻塞 - 异步,保持一个线程一秒三次,则无关紧要,您永远不会为每个请求提供超过100个请求三秒钟。

但是,如果您使一步异步,那么每个请求突然您的代码只需要两个线程秒,因此您现在可以每三秒钟提供300/2 = 150个请求。

更复杂的答案是可能使其变得更好或更差,具体取决于客户端或Web服务器处理超时的方式,客户端重试请求的速度/频率,代码的可并行化程度,方式昂贵的线程交换等等。如果你试图在同步实现中做200个请求,那么100个将在3秒后通过,剩下的100个将在6秒内完成。在异步实现中,由于它们都在各种异步接口处竞争线程,因此大多数都需要5-6秒才能完成。这就是那个。但是如果这些块可以并行化,那么一些请求可能只需一秒钟即可完成,所以也是如此。

所以在最边缘它有点依赖,但最终容量是线程秒,并且通过标准同步或阻塞异步,它们都是相同的。这不是特定的Clojure,当然还有很多更深入的资源,详细说明了所有边缘情况,而不是我在这里提供的。