Rate limiting core.async channels in Clojure
我正在将Clojure与core.async一起使用,并且遇到一种情况,我想对通过通道处理的消息数设置速率限制。
我特别想:
- 定义速率限制,例如 每秒1,000条消息
- 只要消息数量少于速率限制,就可以正常(及时)处理消息
- 如果超出了速率限制,请对事件进行某种明智的替代处理(例如,告诉客户稍后重试)
- 具有相当低的开销
实现此目标的最佳方法是什么?
问题分类:
比速率限制
稍后再试)
我正在通过简单地在循环中组成通道的解决方案来解决这个问题。
一种常见的速率限制算法称为令牌桶。您有固定大小的令牌桶,并以固定速率添加令牌。只要您有令牌,就可以发送消息。
桶的大小决定了"突发性"(您能以多快的速度赶上最大速率),而速率决定了最大平均速率。这些将是我们代码的参数。
让我们创建一个以给定速率发送消息(无关紧要)的通道。 (#1)
1 2 3 4 5 6 7 8 | (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)
1 2 3 4 5 6 7 | (defn limit-chan [in rc] (let [c (chan)] (go (while true (<! rc) ;; wait for token (>! c (<! in)))) ;; pass message along c)) |
现在,如果没有消息等待,我们可以使用默认的这些渠道:
1 2 3 4 5 6 7 | (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)) |
现在,我们已经解决了所有问题。
1 2 3 | (def rchan (-> (chan) (limit-chan (rate-chan 100 1000)) (chan-with-default))) |
就#4而言,这并不是绝对最快的解决方案。但这是使用可组合部件的部件,可能足够快。如果您希望更快,可以做一个循环来完成所有这些操作(而不是将其分解为较小的函数)。最快的方法是自己实现接口。
我写了一个小图书馆来解决这个问题。它的实现与Eric Normand的实现极为相似,但采用了一些针对高吞吐量通道的措施(对于近毫秒的睡眠时间,超时并不精确)。
它还支持对一组通道进行节流,并进行节流。
在这里查看。
这是一种使用原子计数发送的消息并将其定期重置为零的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | (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 |
您将需要更多代码来使用通道中的消息。如果您不确定是否能够以节流的速率持续使用消息,则可能需要指定通道缓冲区的大小或通道类型(丢弃/滑动)。
您在寻找什么被称为断路器。我认为Wikipedia页面的描述很差:
http://en.wikipedia.org/wiki/Circuit_breaker_design_pattern
不过,我们的Scala朋友做的绝对出色:
http://doc.akka.io/docs/akka/2.2.3/common/circuitbreaker.html
还有一个clojure库,但是您必须自己与
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
看起来您可能想要考虑提供Clojure绑定的netflix Hystrix之类的东西:
https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-clj
高温超导