我有一个场景,我想用简单的计数器来监控不同模块的性能。代码是用clojure编写的。在运行期间我需要监视一些未知数量的可能计数器,偶尔我会报告它们(对于statsd)。
这是我的代码:
(defn counter-incrementer []
(let [counters (atom {})
atom-increment (fn [counters-unwrapped metric-name metric-value]
(assoc counters-unwrapped metric-name (+ (get counters-unwrapped metric-name 0) metric-value)))
increment (fn [metric-name metric-value]
(swap! counters atom-increment metric-name metric-value))]
(fn [metric-name metric-value]
(increment metric-name metric-value))))
然后在我要更新计数器的代码中的每个位置,我将使用:
(def inc-fn (counter-incrementer))
.
.
.
(inc-fn "number of logged users" 10)
此代码有效,但我觉得这不是问题的最佳解决方案。例如,每次我想更新一个计数器时,我都会锁定所有计数器地图。
对于clojure中的这类问题,是否有最佳实践解决方案?
答案 0 :(得分:2)
每个计数器都有一个原子是正确的最佳解决方案。
尽管如此,最好的解决方案是采用生产就绪指标库,例如https://github.com/sjl/metrics-clojure
以下是计数器的用法: http://metrics-clojure.readthedocs.org/en/latest/metrics/counters.html
答案 1 :(得分:0)
如果要在一个引用类型中保留多个指标并仅使用clojure,则可以使用代理映射执行此操作。代理程序的更新由代理程序线程池排队和处理,因此调用线程没有锁定。
(def metrics (agent {:counter1 0,:counter2 0}))
(send metrics update-in [:counter1] inc)
@metrics
=> {:counter1 1, :counter2 0}
这样,您可以在需要时动态创建新的键值对。 update-in
会在不在地图中时创建新密钥,但您需要调整更新功能以考虑零值。这可以通过将原始函数与fnil
和默认值合成来实现。
(send metrics update-in [:counter3] (fnil inc 0))
@metrics
=> {:counter1 1, :counter3 1, :counter2 0}
(send metrics update-in [:counter3] (fnil inc 0))
@metrics
=> {:counter1 1, :counter3 2, :counter2 0}
您需要考虑到更新不会直接应用。如果代理池上仍有排队的操作,则会阻止关闭JVM进程,除非给出(shutdown-agents)
。