alibaba / transmittable-thread-local

📌 a missing Java std lib(simple & 0-dependency) for framework/middleware, provide an enhanced InheritableThreadLocal that transmits values between threads even using thread pooling components.
https://github.com/alibaba/transmittable-thread-local
Apache License 2.0
7.54k stars 1.68k forks source link

implement FastThreadLocal mode of Netty #94

Open oldratlee opened 6 years ago

oldratlee commented 6 years ago

xwshiustc commented 6 years ago

考虑如下实现

在现有transmittable-thread-local-x.x.x.jar的基础上新开发第二个可选jar:netty-ttl-plugin.jar(这次新开发),并且netty-ttl-plugin.jar依赖transmittable-thread-local-X.X.X.jar(ttl已有)和netty-all-X.X.X.jar(netty开源).

注意到io.netty.util.concurrent.FastThreadLocal继承自Object,和java.lang.ThreadLocal没有任何继承关系,考虑在netty-ttl-plugin.jar中新建一个io.netty.util.concurrent.FastThreadLocal的子类TtlFastThreadLocal,该类模拟InheritableThreadLocalTransmittableThreadLocal的部分行为,使其拥有

  1. 和InheritableThreadLocal类似的,在Thread构造方法调用init方法时返回parentValue的功能 实现和InheritableThreadLocal想雷同的逻辑
  2. 模仿Transmittable相同的逻辑 提供private static TtlFastThreadLocal<Map< TtlFastThreadLocal<?>, ?>> holder 来实现父子线程的内容传递和还原

因为FastThreadLocal需要配合io.netty.util.concurrent.FastThreadLocalThread才能发挥其fast的威力,观察FastThreadLocalThread它直接继承自Thread,并且内部拥有一个private InternalThreadLocalMap threadLocalMap来保存FastThreadLocal的键值对,我们可以继续再写一个子类继承TtlFastThreadLocalThread继承FastThreadLocalThread,然后添加成员变量private InternalThreadLocalMap ttlThreadLocalMap 来保存新的TtlFastThreadLocal键值对,同时改变其所有的构造方法,所有构造方法加入init方法,init方法大体逻辑如下

public void init(){

        Thread parent = currentThread();
        if (parent.ttlThreadLocalMap != null){
            this.ttlThreadLocalMap =ThreadLocal.createInternalThreadLocalMap(parent.ttlThreadLocalMap);
        }
       ... ... //可能还有其他逻辑
 }

实现TtlFastThreadLocal内具备InheritableThreadLocal相似功能。

TtlFastThreadLocalThread 设置为final类不允许被继承,同时TtlFastThreadLocalThread 代理其run方法使其所有run方法拥有类似ttlRunable中的如下代码块大概主体逻辑

public TtlFastThreadLocalThread  extends FastThreadLocalThread{

    FastThreadLocalThread fastThreadLocalThread;
    private InternalThreadLocalMap ttlThreadLocalMap;

    public void run(){

            Object backupFastTtl = replay(capturedFastTtl);

        try {
             fastThreadLocalThread.run();
            } finally {
            restoreFastTtl(backupFastTtl);
            }
    }

    ......
}

从上一线程复制引用,传递给下一个线程 ,并在使用后还原。以上为主体逻辑保持,使用方式:

FastThreadLocal t = new TtlFastThreadLocal();
t.put("1");
FastThreadLocalThread ftlt = new TtlFastThreadLocalThread(原来的FastThreadLocalThread);---标记1

因为netty的限制,想用FastThreadLocal的fast特性则代码中一定要使用FastThreadLocalThread配合他,两者在fast情况下必然成对出现,所以,我们只要考虑FastThreadLocalThread的改造即可,不用考虑普通的Runnable Callable 怎么实现!

在fastThreadLocal的slow模式下((Thread.currentThread() instanceof FastThreadLocalThread) ==false) 其内部靠的是普通的ThreadLocal实现, 所以我们只要在TtlFastThreadLocal内部slow调用实现全部从ThreadLoad替换为TransmittableThreadLocal即可。 【目前直觉感觉可行】

transmittable-thread-local-x.x.x.jar的TtlAgent只加强FastThreadLocalThread即可,用雷同TtlRunnable.get(Runnable runnable)的方式在 TtlFastThreadLocalThread.get(FastThreadLocalThread)的方式加强实现FastThreadLocalThread的代理。故上面“---标记1”处的方法也可以改为该静态方法

transmittable-thread-local-x.x.x.jar的TtlAgent的字节码加强的治入点仍然和原来的实现相似比如java.util.concurrent.ThreadPoolExecutor的一些列方法,但是假如逻辑判断,如果外部传入线程Runnable s实现为FastThreadLocalThread的子类的情况下,使用TtlFastThreadLocalThread.get(FastThreadLocalThread)的加强方式加强线程,其他情况仍然调用字节码加强TtlRunnable get(Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent)

当业务程序只需要ttl不需要ttlFastThreadLocal的时候,只使用transmittable-thread-local-x.x.x.jar, 如果想用FastThreadLocal的自动传递,可依赖netty-ttl-plugin.jar

xwshiustc commented 6 years ago

理解错误,上面论述是对netty本身使用ttl上下文的方案。 我们要讨论的是如何将netty中的高性能引入到ttl中,上面的方案作废!

driventokill commented 6 years ago

可以考虑把 Ttl 存储接口分离出来,根据运行时环境检测选择具体的实现,有 FastThreadLocal 的环境使用 FastThreadLocal 实现,后续甚至可以考虑让用户自己实现自己的存储方案,通过 spi 的方式加载进来

oldratlee commented 6 years ago

可以考虑把 Ttl 存储接口分离出来

综上,是否做分离 先 提前决策。

@driventokill

driventokill commented 6 years ago

在支持 FastThreadLocal 的环境下,每个 Ttl 创建一个 FastThreadLocal (这个就是我说的想抽出来做成一个存储接口),现在每个 Ttl 的存储其实是 InheritableThreadLocal 实现,所以每次都是使用 super.getsuper.set 来完成值的读取和设置,分别比较一下:

前提

假定每个 Ttl 有一个 FastThreadLocal :

private FastThreadLocal<T> fastThreadLocal = new FastThreadLocal<>();

读取

现有实现

public final T get() {
        T value = super.get();
        if (null != value) {
            addValue();
        }
        return value;
    }

FastThreadLocal

public final T get() {
        //T value = super.get();
        T value = fastThreadLocal.get();
        if (null != value) {
            addValue();
        }
        return value;
    }

设置

现有实现

public final void set(T value) {
        super.set(value);
        if (null == value) { // may set null to remove value
            removeValue();
        } else {
            addValue();
        }
    }

FastThreadLocal

public final void set(T value) {
        //super.set(value);
        fastThreadLocal.set(value);
        if (null == value) { // may set null to remove value
            removeValue();
        } else {
            addValue();
        }
    }

清理

现有实现

public final void remove() {
        removeValue();
        super.remove();
    }

FastThreadLocal

public final void remove() {
        removeValue();
        //super.remove();
        fastThreadLocal.remove();
    }
xwshiustc commented 6 years ago

目前Ttl jar在xbootclasspath中,而 FastThreadLocal是第三方jar,感觉上netty不应也融入xbootclasspath,所以简单地在TransmittableThreadLocal中引入 FastThreadLocal 不知道是否可以。

driventokill commented 6 years ago

简单地在TransmittableThreadLocal中引入 FastThreadLocal 不知道是否可以

实现的时候并不会这么简单,需要对环境检测一下,只是在运行时有 FastThreadLocal 支持的情况下才会创建。我上面写法只是为了表达更清晰一点

@xwshiustc

xwshiustc commented 6 years ago

明白 理解了。

IgorKey commented 4 years ago

@driventokill @oldratlee guys, could you please give the status of this issue? I tried this on netty, ofc it didn't work. Are there any workarounds for use this brilliant lib with netty fastThreads?