> http-kit作为HTTP服务器和客户端(非阻塞)
> monger作为我的DB驱动程序(阻塞)
> 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)) )
我打算做的是在thread
块中包含任何涉及IO的调用,并在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 )))
这样做有什么意义吗?
我这样做是因为这是我发现的最佳实践,但它们的性能优势对我来说仍然是一个谜.我认为同步堆栈的问题是它们每个请求使用一个线程.现在看来他们使用不止一个.
在此先感谢您的帮助,祝您有个美好的一天.
简单的答案是否定的,你不会以这种方式增加容量.如果你有内存可以容纳100个线程,那么每3秒间隔就有300个“线程秒”的容量.所以,假设每个块需要一秒钟才能执行.如果每个请求同步运行,保持线程整整三秒,或者阻塞 – 异步,保持一个线程一秒钟三次,则无关紧要,您将永远不会每三秒钟提供超过100个请求.但是,如果您使一步异步,则突然您的代码每个请求只需要两个线程秒,因此您现在可以每三秒提供300/2 = 150个请求.
更复杂的答案是它可能会变得更好或更糟,取决于客户端或Web服务器处理超时的方式,客户端重试请求的速度/频率,代码的可并行化程度,线程交换的成本等等.如果您尝试在同步实现中执行200个请求,然后100秒将在3秒后通过,剩余的100个将在6秒内完成.在异步实现中,由于它们都在各种异步接口处竞争线程,因此大多数都需要5-6秒才能完成.就是这样.但是如果这些块是可并行化的,那么一些请求可能只需一秒钟即可完成,所以也就是这样.
所以在最有利的方面它取决于,但最终容量是线程秒,并且通过标准同步或阻塞 – 异步,它们都是相同的.这不是特定的Clojure,并且肯定有很多更深入的资源详细说明了所有边缘情况,而不是我在这里提供的.