likes
comments
collection
share

LogbackMDC 2022年有变动?

作者站长头像
站长
· 阅读数 82

今天本来在研究 线程传递参数的方式,ThreadLocal MDC,来一篇吧。。

LogbackMDC 2022年有变动?

MDC 是每个线程 都有一个么?

是的,百度一下 都知道,但是 怎么证明? 首先

MDC.put  -》 
mdcAdapter.put(key, val);   -》 

MDCAdapter 实现类 LogbackMDCAdapter 

LogbackMDC 2022年有变动?

LogbackMDC 2022年有变动?

上面第50行 可以看到 他获取之前最后一次的 标识,可以没有么?它起到了什么作用?

//这里 它传入的是 1
private Integer getAndSetLastOperation(int op) {
    Integer lastOp = (Integer)this.lastOperation.get();
    this.lastOperation.set(op);
    return lastOp;
}

外面

public void put(String key, String val) throws IllegalArgumentException {
    if (key == null) {
        throw new IllegalArgumentException("key cannot be null");
    } else {
        Map<String, String> oldMap = (Map)this.copyOnThreadLocal.get();
        //设置为 1,并且 返回原来的旧值 代表着上次是什么操作
        Integer lastOp = this.getAndSetLastOperation(1);
        //上次是写操作 并且oldMap 不是null 就在oldMap 基础 上put
        if (!this.wasLastOpReadOrNull(lastOp) && oldMap != null) {
            oldMap.put(key, val);
        } else {
            Map<String, String> newMap = this.duplicateAndInsertNewMap(oldMap);
            newMap.put(key, val);
        }

    }
}

//需要返回 false 才代表 之前有oldMap 操作,这里的判断 不是null 不是2,看了一遍代码 这不就是1么
private boolean wasLastOpReadOrNull(Integer lastOp) {
    return lastOp == null || lastOp == 2;
}

这就需要去看看 什么时候 会lastOp 为2

LogbackMDC 2022年有变动?

如果是 2就是上次操作是 呢,这个时候 有put 操作 就 新创建一个空的newMap 赋值,如果原来是 1 代表这个线程 上次就是 put ,在原来的oldMap 基础上 进行put。

上面这个看完 会不会有一个疑问,那我多次 put (a,b 属性)然后 get下,在put a属性,难道我get b就没有了?

LogbackMDC 2022年有变动?

返回 a 和 b

一debug ,调用的是另一个获取方法,那什么时候 调用这个赋值为2的方法呢?

LogbackMDC 2022年有变动?

接口中没有这个方法,留个疑问 继续往下

LogbackMDC 2022年有变动?

Thread 和 ThreadLocal的关系 是一对一么?

上面说到 Thread 中有 ThreadLocal.ThreadLocalMap

LogbackMDC 2022年有变动?

变化

上面的东西 看完,感觉应该结尾了吧。。。NO, 2022年这个put 方法有变化了。。。。

LogbackMDC 2022年有变动?

往下继续看 注释 fix LOGBACK-1684 using code from LOGBACK-620

既然你这么说了 我就分别看看 这两个

jira.qos.ch/browse/LOGB…

代码改动在 github.com/qos-ch/logb…

为什么改动呢 保持原版不香么

大概意思 在对我们的应用程序进行性能分析时,LogbackMDCAdapter显示为性能热点。这是因为应用程序确实经常替换MDC中的多个条目,例如 删除/写入而不使用任何中间日志语句。另外,在生产环境中,在创建LoggingEvent之前经过过滤的实际日志消息相对较少。我重做了实现,以提高性能。其主要思想是尽可能推迟克隆内部Map。该补丁在该测试中提高了应用程序的总体性能约10%

LogbackMDC 2022年有变动?

里面 为了替换原来的 flag 判断,现在多了

final ThreadLocal<Map<String, String>> readWriteThreadLocalMap = new ThreadLocal<Map<String, String>>();
final ThreadLocal<Map<String, String>> readOnlyThreadLocalMap = new ThreadLocal<Map<String, String>>();

讨论

下面评论区 Ceki 大佬提出,你现在 用了2个对象,是不是可以通过

Get操作从来不需要复制或连续的“put/remove”操作,只有Get后面跟着“put/remove”操作需要复制map

这样就可以 so on Note that 8 operations were performed by only 3 maps were created

但是 Michael 大佬说

但作为一名软件开发人员,我对你的策略感到有点不舒服:“读后写后复制”听起来与“写后复制”和“惰性复制”不太一样。使用您的方法,您将迫使开发人员关注放置和获取操作的顺序,特别是如果应用程序在写入MDC之前执行存在性检查。

LogbackMDC 2022年有变动?

Ceki 大佬 最后说 经过测试 你写的确实不错 哈哈

LogbackMDC 2022年有变动?

彩蛋

版本升级后

//这个方法 原来是 赋值为2的,现在修改了
public Map<String, String> getPropertyMap() {
    Map<String, String> readOnlyMap = readOnlyThreadLocalMap.get();
    if (readOnlyMap == null) {
        Map<String, String> current = readWriteThreadLocalMap.get();
        if (current != null) {
            final Map<String, String> tempMap = new HashMap<String, String>(current);
            readOnlyMap = Collections.unmodifiableMap(tempMap);
            readOnlyThreadLocalMap.set(readOnlyMap);
        }
    }
    return readOnlyMap;
}

像不像 Eureka 的多级缓存机制,都一个道理

里面一个点 是 使用了 Collections.unmodifiableMap,所有对外返回的list map等引用参数,都要重新赋值一遍 / 只读不能操作。防止 外部改变这个元素 影响我们。

不可变类 也是一个道理,返回需要 重新生成一个,不会直接返回 原来的元素,不可变类 防止的是 一个对象中有20个属性, 防止20个属性出现中间态,比如 5个属性 属于v1, 15个属性 属于v2 这种中间态情况。

转载自:https://juejin.cn/post/7159888672156811294
评论
请登录