我不知道这是否是一个错误还是预期行为。我一直调查一个奇怪的情况,Clojure 的绑定传送方式并没有像我们预期的那样运行。下面的复现案例显示了发生的问题,仅使用 clojure.core
函数。实际上,我们已经使用 clojure.lang.Agent/soloExecutor
作为 Langohr RabbitMQ 订阅的 ExecutorService
。处理 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
在线程局部存储中设置 threadBindingFrame,并且 never un-sets it。
如果相同的线程随后被用于其他目的,动态绑定将保持从上一个对线程使用时的状态。
复现案例
(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
Gist