我正在尝试确定使用Cacheable ThreadPools或Spring的异步注释时线程安全MDC
的方式。
我有一个方法可以调用多个CompletableFuture<>
并使用线程池
@Async
public CompletableFuture<List> someMethod(String request) {
try {
MDC.put("request", request)
MDC.put("loggable1", "loggable1");
MDC.put("loggable2", "loggable2");
log.info("Log Event");
} finally {
MDC.clear();
}
}
Logback的MDCAdapter中的相关部分
final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal<Map<String, String>>();
public void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
Map<String, String> oldMap = copyOnThreadLocal.get();
Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);
if (wasLastOpReadOrNull(lastOp) || oldMap == null) {
Map<String, String> newMap = duplicateAndInsertNewMap(oldMap);
newMap.put(key, val);
} else {
oldMap.put(key, val);
}
}
public void clear() {
lastOperation.set(WRITE_OPERATION);
copyOnThreadLocal.remove();
}
public void remove(String key) {
if (key == null) {
return;
}
Map<String, String> oldMap = copyOnThreadLocal.get();
if (oldMap == null)
return;
Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);
if (wasLastOpReadOrNull(lastOp)) {
Map<String, String> newMap = duplicateAndInsertNewMap(oldMap);
newMap.remove(key);
} else {
oldMap.remove(key);
}
}
由于ThreadPools重用已经生成的线程,而MDC使用ThreadLocal上下文映射。我们是否有可能丢失或损坏存储在MDC中的值?如果是这样,那么可能会发生什么样的情况?
答案 0 :(得分:0)
我只是想说我们看到的东西看起来像您所描述的一样可疑。我还没有确切的证据,但是在代码中爬网似乎需要花费一些时间将事件刷新到附加程序,这可能导致在线程池清理例程运行后读取MDC,尤其是因为我们正在刷新日志到Kafka(网络I / O通常比系统上任何东西都要慢得多)。他们使用本地写时复制可继承线程来存储MDC映射本身,但是我不确定例如它将如何响应MDC.clear()。我们有很多非常短暂的任务可以并行执行,因此在混合使用Kafka的情况下,似乎很可能导致竞争。
此方法的另一面-随每个条目存储地图副本-似乎可能无法缩放(可能导致内存耗尽,GC崩溃)。我目前正在寻找中间立场。