使用ThreadPools或Spring Async记录MDC

时间:2016-10-07 01:45:49

标签: java logback mdc

我正在尝试确定使用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中的值?如果是这样,那么可能会发生什么样的情况?

1 个答案:

答案 0 :(得分:0)

我只是想说我们看到的东西看起来像您所描述的一样可疑。我还没有确切的证据,但是在代码中爬网似乎需要花费一些时间将事件刷新到附加程序,这可能导致在线程池清理例程运行后读取MDC,尤其是因为我们正在刷新日志到Kafka(网络I / O通常比系统上任何东西都要慢得多)。他们使用本地写时复制可继承线程来存储MDC映射本身,但是我不确定例如它将如何响应MDC.clear()。我们有很多非常短暂的任务可以并行执行,因此在混合使用Kafka的情况下,似乎很可能导致竞争。

此方法的另一面-随每个条目存储地图副本-似乎可能无法缩放(可能导致内存耗尽,GC崩溃)。我目前正在寻找中间立场。