我不确定这是不是一个bug,还是预期的行为。我已经调查了一个奇特的案例,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*
的值。
观察到的行为的原因在于 binding-conveyor-fn
如何设置线程绑定的方式,以及在send-via
中的原因。binding-conveyor-fn
设置了线程绑定到线程本地存储中,并且永远不会将其解除。
如果相同的线程随后被用于其他目的,则之前的线程使用中的动态绑定将保留。
复现案例
(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
代码片段