TransmittableThreadLocal使用JavaAgent动态代理机制分析

本文不提及TTL的作用,仅从源码层面分析TTL是怎么使用JavaAgent来动态代理的
依赖如下:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.11.4</version>
</dependency>

JavaAgent字节码方式增强

代码入口

com.alibaba.ttl.threadpool.agent.TtlAgent

使用方式

启动jar的时候,附加上参数-javaagent:/xx/transmittable-thread-local.jar(目录自己放好)

/opt/soft/jdk/jdk1.8.0_191/bin/java
    -javaagent:/xx/transmittable-thread-local.jar 
    -jar /opt/xxx.jar

源码分析

执行步骤

第一步:解析参数(可不传),将“xx:xx”解析为Map<String, String>

常用参数:
ttl.agent.disable.inheritable.for.thread.pool:true
ttl.agent.logger:STDOUT
ttl.agent.enable.timer.task:false

第二步:设置日志输出参数类型,比如STDOUT(不设置默认就是STDOUT, loggerImplType = 0)

STDOUT: 标准输出
STDERR: 标准错误输出

第三步:组装JavassistTransformlet,分为三种类型如下

TtlExecutorTransformlet处理的类:ThreadPoolExecutor及其子类、ScheduledThreadPoolExecutor
TtlForkJoinTransformlet处理的类:ForkJoinTask、ForkJoinPool
默认开启。TtlTimerTaskTransformlet处理的类:仅处理TimeTask子类

第四步:调用生效的时机到了

执行生效方法:inst.addTransformer(transformer, true);

TtlAgent入口premain()方法

Java中的javaAgent一般会涉及到 MANIFEST.MF配置类、premain()方法入口,下面来解析

  • MANIFEST.MF配置如下:

可见Premain-Class指向了com.alibaba.ttl.threadpool.agent.TtlAgent类

Manifest-Version: 1.0
Created-By: Apache Maven 3.5.4
Built-By: jerry
Build-Jdk: 11.0.1
Boot-Class-Path: transmittable-thread-local-2.10.2.jar
Can-Redefine-Classes: false
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: false
Premain-Class: com.alibaba.ttl.threadpool.agent.TtlAgent

接来下解析TllAgent.java入口类,其premain()方法如下

/** TTL javaAgent字节码增强premain入口类 **/
public static void premain(String agentArgs, @NonNull Instrumentation inst) {
    // 第一步:解析参数(可不传),将“xx:xx”解析为Map<String, String>
    // 常用参数:ttl.agent.disable.inheritable.for.thread.pool:true
    //         ttl.agent.logger:STDOUT
    //         ttl.agent.enable.timer.task:false
    kvs = splitCommaColonStringToKV(agentArgs);

    // 第二步:设置日志输出参数类型,比如STDOUT(不设置默认就是STDOUT, loggerImplType = 0)
    // - STDOUT: 标准输出
    // - STDERR: 标准错误输出
    Logger.setLoggerImplType(getLogImplTypeFromAgentArgs(kvs));
    final Logger logger = Logger.getLogger(TtlAgent.class);

    try {
        logger.info("[TtlAgent.premain] begin, agentArgs: " + agentArgs + ", Instrumentation: " + inst);
        // 获取是否开启 "关闭继承" 的能力,不传默认返回false
        final boolean disableInheritableForThreadPool = isDisableInheritableForThreadPool();

        // 第三步:组装JavassistTransformlet
        final List<JavassistTransformlet> transformletList = new ArrayList<JavassistTransformlet>();
        // TtlExecutorTransformlet处理的类:ThreadPoolExecutor及其子类、ScheduledThreadPoolExecutor
        transformletList.add(new TtlExecutorTransformlet(disableInheritableForThreadPool));
        // TtlForkJoinTransformlet处理的类:ForkJoinTask、ForkJoinPool
        transformletList.add(new TtlForkJoinTransformlet(disableInheritableForThreadPool));
        // 默认开启。TtlTimerTaskTransformlet处理的类:仅处理TimeTask子类
        if (isEnableTimerTask()) transformletList.add(new TtlTimerTaskTransformlet());

        // 第四步:调用生效的时机到了
        final ClassFileTransformer transformer = new TtlTransformer(transformletList);
        // -- 调用到了这里,最终会调用到TtlTransformer#transform()方法
        // -- 备注:TtlTransformer继承了ClassFileTransformer
        inst.addTransformer(transformer, true);
        logger.info("[TtlAgent.premain] addTransformer " + transformer.getClass() + " success");

        logger.info("[TtlAgent.premain] end");

        ttlAgentLoaded = true;
    } catch (Exception e) {
        String msg = "Fail to load TtlAgent , cause: " + e.toString();
        logger.log(Level.SEVERE, msg, e);
        throw new IllegalStateException(msg, e);
    }
}

接下来解析上面几个步骤~~~

第一步:解析参数(可不传),将“xx:xx”解析为Map<String, String>
  • 入口:TtlAgent#premain() -> splitCommaColonStringToKV()
kvs = splitCommaColonStringToKV(agentArgs);
  • 具体解析
/**
 * 将创建来的字符串参数解析成map
 * eg:ttl.agent.disable.inheritable.for.thread.pool:true 解析成 Map{"ttl.agent.disable.inheritable.for.thread.pool":"true"}
 * Split to {@code json} like String({@code "k1:v1,k2:v2"}) to KV map({@code "k1"->"v1", "k2"->"v2"}).
 */
static Map<String, String> splitCommaColonStringToKV(@Nullable String commaColonString) {
    Map<String, String> ret = new HashMap<String, String>();
    if (commaColonString == null || commaColonString.trim().length() == 0) return ret;

    final String[] splitKvArray = commaColonString.trim().split("\\s*,\\s*");
    for (String kvString : splitKvArray) {
        final String[] kv = kvString.trim().split("\\s*:\\s*");
        if (kv.length == 0) continue;
        if (kv.length == 1) ret.put(kv[0], "");
        else ret.put(kv[0], kv[1]);
    }
    return ret;
}
第二步:设置日志输出参数类型,比如STDOUT(不设置默认就是STDOUT, loggerImplType = 0)
  • 入口:TtlAgent#premain() -> getLogImplTypeFromAgentArgs()
/** 入口类 **/
public static void premain(String agentArgs, @NonNull Instrumentation inst) {
    ....
    // 设置参数类型,比如STDOUT(不设置默认就是STDOUT, loggerImplType = 0)
    // - STDOUT: 标准输出
    // - STDERR: 标准错误输出
    Logger.setLoggerImplType(getLogImplTypeFromAgentArgs(kvs));
    final Logger logger = Logger.getLogger(TtlAgent.class);
    ....
}
private static String getLogImplTypeFromAgentArgs(@NonNull final Map<String, String> kvs) {
    return kvs.get(Logger.TTL_AGENT_LOGGER_KEY);
}
/**
 * eg:STDOUT
 */
public static void setLoggerImplType(String type) {
    // 用loggerImplType记录已设置了
    if (loggerImplType != -1) {
        throw new IllegalStateException("TTL logger implementation type is already set! type = " + loggerImplType);
    }
    // 不设置,默认模式就是STDOUT, loggerImplType = 0
    if (STDERR.equalsIgnoreCase(type)) loggerImplType = 0;
    else if (STDOUT.equalsIgnoreCase(type)) loggerImplType = 1;
    else loggerImplType = 0;
}
第三步(重点):组装JavassistTransformlet,分为三种类型如下

主要分为三个JavassistTransformlet如下

TtlExecutorTransformlet处理的类:ThreadPoolExecutor及其子类、ScheduledThreadPoolExecutor
TtlForkJoinTransformlet处理的类:ForkJoinTask、ForkJoinPool
默认开启。TtlTimerTaskTransformlet处理的类:仅处理TimeTask子类

  • 入口:TtlAgent#premain() ->
/** 入口类 **/
public static void premain(String agentArgs, @NonNull Instrumentation inst) {
    ....
    // JavassistTransformlet用于转换
    final List<JavassistTransformlet> transformletList = new ArrayList<JavassistTransformlet>();
    // TtlExecutorTransformlet处理的类:ThreadPoolExecutor及其子类、ScheduledThreadPoolExecutor
    transformletList.add(new TtlExecutorTransformlet(disableInheritableForThreadPool));
    // TtlForkJoinTransformlet处理的类:ForkJoinTask、ForkJoinPool
    transformletList.add(new TtlForkJoinTransformlet(disableInheritableForThreadPool));
    // 默认开启。TtlTimerTaskTransformlet处理的类:仅处理TimeTask子类
    if (isEnableTimerTask()) transformletList.add(new TtlTimerTaskTransformlet());
    ....
}
  • 接下来分析三个transformletList如下
    • TtlExecutorTransformlet
@Override
public void doTransform(@NonNull final ClassInfo classInfo) throws IOException, NotFoundException, CannotCompileException {
    if (EXECUTOR_CLASS_NAMES.contains(classInfo.getClassName())) {
        // ThreadPoolExecutor、ScheduledThreadPoolExecutor
        // 获取待增强的类class
        final CtClass clazz = classInfo.getCtClass();

        // 遍历所有的方法,目的是为了判断参数是否Runnable、Callable。然后给外层包裹TtlRunnable、TtlCallable
        for (CtMethod method : clazz.getDeclaredMethods()) {
            updateSubmitMethodsOfExecutorClass_decorateToTtlWrapperAndSetAutoWrapperAttachment(method);
        }

        // 判断是否需要禁用"继承"能力
        if (disableInheritableForThreadPool)
            // 禁用====================================
            updateConstructorDisableInheritable(clazz);

        // 标记为已修改过
        classInfo.setModified();
    } else {
        // 非ThreadPoolExecutor、非ScheduledThreadPoolExecutor 会走到这步
        final CtClass clazz = classInfo.getCtClass();

        // 过滤掉 基本数据类型、数组、接口、注解
        if (clazz.isPrimitive() || clazz.isArray() || clazz.isInterface() || clazz.isAnnotation()) {
            return;
        }
        // 过滤掉 非ThreadPoolExecutor子类数据
        if (!clazz.subclassOf(clazz.getClassPool().get(THREAD_POOL_EXECUTOR_CLASS_NAME))) return;

        logger.info("Transforming class " + classInfo.getClassName());
        // 走到这步,说明这个类就算不是ThreadPoolExecutor,也是继承了ThreadPoolExecutor,是他的子类
        // -- 尝试追加执行前后的方法,都会追加unwrapIfIsAutoWrapper()来包裹
        // -- 其实就是加一个执行前后的方法beforeExecute、afterExecute
        // ---- beforeExecute:用TtlRunnable.unwrap()包裹
        // ---- afterExecute:直接返回Runnable,不包裹
        final boolean modified = updateBeforeAndAfterExecuteMethodOfExecutorSubclass(clazz);
        // 如果修改成功,就设置为 this.modified = true;
        if (modified) classInfo.setModified();
    }
}
    • TtlForkJoinTransformlet
@Override
public void doTransform(@NonNull final ClassInfo classInfo) throws IOException, NotFoundException, CannotCompileException {
    if (FORK_JOIN_TASK_CLASS_NAME.equals(classInfo.getClassName())) {
        // 处理ForkJoinTask
        // 这个最终来直接用try catch来拼接 备份->run()->重放  的代码
        updateForkJoinTaskClass(classInfo.getCtClass());
        classInfo.setModified();
    } else if (disableInheritableForThreadPool && FORK_JOIN_POOL_CLASS_NAME.equals(classInfo.getClassName())) {
        // 禁用"继承"能力开启 并且 类型是ForkJoinPool
        updateConstructorDisableInheritable(classInfo.getCtClass());
        classInfo.setModified();
    }
}
    • TtlTimerTaskTransformlet
@Override
public void doTransform(@NonNull final ClassInfo classInfo) throws IOException, NotFoundException, CannotCompileException {
    // java.util.TimerTask不处理,直接跳过。只会处理他的子类
    if (TIMER_TASK_CLASS_NAME.equals(classInfo.getClassName())) return; // No need transform TimerTask class

    final CtClass clazz = classInfo.getCtClass();
    // 过滤掉 基本数据类型、数组、接口、注解
    if (clazz.isPrimitive() || clazz.isArray() || clazz.isInterface() || clazz.isAnnotation()) {
        return;
    }
    // class contains method `void run()` ?
    try {
        // 只处理方法包含"void run()",必须要返回void
        final CtMethod runMethod = clazz.getDeclaredMethod(RUN_METHOD_NAME, new CtClass[0]);
        if (!CtClass.voidType.equals(runMethod.getReturnType())) return;
    } catch (NotFoundException e) {
        return;
    }
    // 非java.util.TimerTask子类不处理
    if (!clazz.subclassOf(clazz.getClassPool().get(TIMER_TASK_CLASS_NAME))) return;

    logger.info("Transforming class " + classInfo.getClassName());

    // 更新代码操作
    updateTimerTaskClass(clazz);
    classInfo.setModified();
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值