我不确定这是否是一个错误还是预期的行为。我一直在调查一个奇怪的情况,Clojure 的绑定传递方式并没有按照我们预期的进行。下面的回放案例显示了使用 clojure.core
函数发生的问题。实际上,发生的事情是,我们使用了 clojure.lang.Agent/soloExecutor
作为 ExecutorService
用于 一个 Langohr RabbitMQ 订阅。处理 RabbitMQ 消息的线程能够读取设置在无关代码中的动态变量,这些代码是通过代理运行的。
症状
我们是这样设置动态变量的
(defn http-handler [url]
(binding [*url* url]
(log/info "doing a http request")))
(defn rabbit-handler [message]
(log/info "handling a RabbitMQ message"))
我们的日志系统会抓取当前 *url*
的值并将其添加到日志行中。当我们看到日志行中来自其他无关线程对 *url*
进行记录时,这个变量应该没有值。在上面的玩具示例中,我们看到了在 "处理 RabbitMQ 消息"
的日志行中设置 *url*
的值。
观察到的行为的原因是 在 send-via
中的函数和 在 binding-conveyor-fn
设置线程绑定的方式中。 binding-conveyor-fn
设置 thread-local 存储中的 threadBindingFrame,并且从不在设置后解除设置。
如果相同线程随后用于其他目的,上一次使用线程时设置的动态绑定将保持不变。
复现案例
(ns binding
(:import java.util.concurrent.Executors))
(def thread-pool
(Executors/newFixedThreadPool 1))
(set-agent-send-off-executor! thread-pool)
(def counter (agent 0))
(add-tap println)
(def ^:dynamic *some-context* nil)
(tap> "start")
(defn inspect []
(tap>
(format "tap: thread=%s context=%s"
(.getId (Thread/currentThread))
*some-context*)))
(binding [*some-context* "in the agent"]
(send-off counter (fn [& _]
(inspect))))
(.submit thread-pool inspect)
;; Output
;; start
;; tap: thread=43 context=in the agent
;; tap: thread=43 context=in the agent
代码片段