我正在使用带有core.async的Clojure,并且我希望对通过频道处理的消息数量设置速率限制。
特别是我想:
实现这一目标的最佳方式是什么?
答案 0 :(得分:15)
问题分解:
我正在通过简单地在循环中组合频道的解决方案来解决问题。
常见的速率限制算法称为Token bucket。您有一个固定大小的令牌桶,您可以固定的速率添加令牌。只要您有令牌,就可以发送消息。
水桶的大小决定了“突发性”(你能达到最大速率的速度),并且速率决定了最大平均速率。这些将是我们代码的参数。
让我们创建一个以给定速率发送消息(无关紧要)的频道。 (#1)
(defn rate-chan [burstiness rate]
(let [c (chan burstiness) ;; bucket size is buffer size
delta (/ 1000 rate)]
(go
(while true
(>! c :go) ;; send a token, will block if bucket is full
(<! (timeout delta)))) ;; wait a little
c))
现在我们想要一个按费率限制另一个频道的频道。 (#2)
(defn limit-chan [in rc]
(let [c (chan)]
(go
(while true
(<! rc) ;; wait for token
(>! c (<! in)))) ;; pass message along
c))
现在,如果没有消息等待,我们可以使用默认的这些频道:
(defn chan-with-default [in]
(let [c (chan)]
(go
(while true
;; take from in, or if not available, pass useful message
(>! c (alts! [in] :default :rate-exceeded))))
c))
现在我们已经完成了解决问题的所有方法。
(def rchan (-> (chan)
(limit-chan (rate-chan 100 1000))
(chan-with-default)))
就#4而言,这不是绝对最快的解决方案。但它是一个使用可组合部件并且可能足够快的部件。如果你想要它更快,你可以做一个循环来完成所有这些(而不是将它分解成更小的函数)。最快的是自己实施interfaces。
答案 1 :(得分:7)
我写了a little library来解决这个问题。它的实现与Eric Normand相似,但是对于高吞吐量通道采取了一些措施(对于接近毫秒的睡眠时间,超时并不精确)。
它还支持全局限制一组通道,并支持函数限制。
查看here。
答案 2 :(得分:6)
这是使用atom计算发送消息的数量并定期将其重置为零的一种方法:
(def counter (atom 0))
(def time-period 1000) ;milliseconds
(def max-rate 1000) ;max number of messages per time-period
(def ch (chan))
(defn alert-client []
(println "That's enough!"))
(go (while true (<! (timeout time-period)) (reset! counter 0))) ; reset counter periodically
(defn process [msg]
(if (> (swap! counter inc) max-rate) (alert-client) (put! ch msg)))
(doseq [x (range 1001)] (process x)) ; throw some messages at the channel
您需要使用更多代码来使用来自频道的消息。如果您不确定是否能够按照限制它们的速率持续使用消息,则可能需要指定通道缓冲区大小或通道类型(丢弃/滑动)。
答案 3 :(得分:4)
您正在寻找的是断路器。我认为维基百科的页面描述很差:
http://en.wikipedia.org/wiki/Circuit_breaker_design_pattern
尽管如此,我们的Scala朋友们做得非常棒:
http://doc.akka.io/docs/akka/2.2.3/common/circuitbreaker.html
还有一个clojure库,但您必须自己与core.async
集成:
https://github.com/krukow/clojure-circuit-breaker
https://github.com/josephwilk/circuit-breaker
关于断路器和使用clojure扩展的博客文章:
http://blog.josephwilk.net/clojure/building-clojure-services-at-scale.html
看起来你可能想要考虑像netflix Hystrix 这样提供clojure绑定的东西:
https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-clj
HTH