8
如何在线程池中使用MDC?
在我们的软件中,我们广泛使用MDC来跟踪Web请求的内容,例如会话ID和用户名。在原始线程中运行时,这可以正常工作。但是,有很多事情需要在后台处理。为此,我们使用java.concurrent.ThreadPoolExecutor和java.util.Timer类以及一些自卷式异步执行服务。所有这些服务都管理自己的线程池。 这是Logback手册关于在这样的环境中使用MDC的内容: 映射的诊断上下文的副本不能始终由工作线程从发起线程继承。当java.util.concurrent.Executors用于线程管理时,就是这种情况。例如,newCachedThreadPool方法创建ThreadPoolExecutor,并且像其他线程池代码一样,它具有复杂的线程创建逻辑。 在这种情况下,建议在将任务提交给执行者之前,在原始(主)线程上调用MDC.getCopyOfContextMap()。当任务运行时,作为第一个动作,它应调用MDC.setContextMapValues()将原始MDC值的存储副本与新的Executor托管线程相关联。 这样做很好,但是忘记添加这些呼叫非常容易,并且直到太晚之前,都没有简便的方法来识别问题。使用Log4j的唯一迹象是您在日志中丢失了MDC信息,而使用Logback则获得了陈旧的MDC信息(因为胎面池中的线程从其上运行的第一个任务继承了其MDC)。两者都是生产系统中的严重问题。 我认为我们的处境没有任何特别之处,但是我在网络上找不到太多有关此问题的信息。显然,这不是很多人反对的事情,因此必须有一种避免的方法。我们在这里做错了什么?