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.59k stars 1.69k forks source link

支持运行时加载TTL Agent(aka. Agent Attach使用方式 ) #169

Open zscgrhg opened 4 years ago

zscgrhg commented 4 years ago
oldratlee commented 4 years ago

支持运行加载agent对于TTL是一个可以实现的功能Feature。

是否实现这个Feature,需要一起讨论一下:

对比 『启动时加载-javaagent选项』方式 与 运行时加载方式 2者。

下面展开说明对比:

1. 两者都需要准备好 TTL Agent Jar

这点应该是确定的。(如果不是,可以给一下资料)

2. 在Agent Jar要准备的前提下,『启动时』加载方式 比 『运行时』加载方式 使用更简单

动态Attach需要

参见:

3. 因为TTL改了jdk标准类,动态加载流程更复杂,更多的可靠性风险

当然,这点不是大问题,对要实现的逻辑都必须要保证好可靠性/正确性。 :)

4. Agent动态加载会触发JVM JIT退化,应用过载挂掉风险

这边线上出过因为Agent动态注入,线上应用性能劣化,结果应用过载挂掉情况。 这样的风险 也是非常大。


综上,目前个人觉得 不是很有必要 / 不推荐 实现TTLAgent运行时加载方式。 @zscgrhg

欢迎大家讨论 😁

PS: 已有的issue

zscgrhg commented 4 years ago

我的场景是这样的:想实现一个JUnit Rule,以简化单元测试的开发。

比如我有一个Service:

@Service
public class ServiceA {
    @Autowired
    DBOpertation serviceB;
    @Autowired
    RpcClinetA rpcClinetA;

    public int doBusiness(int x) {
        serviceB.dbOps();
        //跟踪多线程调用需要 TTL 库的支持
        List<Object> rpcResult = IntStream.range(1, x).parallel()
                .mapToObj(i -> rpcClinetA.sendHttp())
                .collect(Collectors.toList());
        return x;
    }
}

我的测试用例会这么写:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceATests {
    @Autowired
    private ServiceA serviceA;
    @Rule
    public final TestRule watchman = new ZUnitWatcher();
    @Test
    public void test1() {
       assert serviceA.doBusiness(1)==1;
    }
}

这个用例会调用数据库和远程服务,我不打算手写mock,我想通过 Rule去跟踪调用链,然后生成 Mock 代码,这里跟踪多线程需要 TTL 库的支持。ZUnitWatcher大致是这样的:

public class ZUnitWatcher extends TestWatcher implements SpecWriter {     

    protected void succeeded(Description description) {
        List<Invocation> invs=getInvocation();
        // 使用捕获的调用链数据生成 Mock代码
        buildUT(invs);
    }
    static {
        if (!TtlAgent.isTtlAgentLoaded()) {
                //加载 TtlAgent
                ZUnit.loadTtlAgent();
                //加载 调用链追踪 Agent
                ZUnit.loadTraceAgent();
        }
    }
}

这种场景下,

zscgrhg commented 4 years ago

@oldratlee

可靠性和正确性的方面的考虑,是不是只要保证 依赖TTL的代码都在TTL转换完成之后执行就没有问题呢?

这点一般都是能够做到的,如果做不到,那就从premain加载就好了。

如果去掉agentmain方法,有些场景易用性就差很多了。

oldratlee commented 4 years ago

我的场景是这样的:想实现一个JUnit Rule,以简化单元测试的开发。

比如我有一个Service: ...

@zscgrhg 关于使用TTL Agent的单元测试开发简化,想到下面的一种解决方法: 在Maven运行单元测试时,配置好TTL AgentJVM参数。

具体设置方式

<plugin>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.22.2</version>
  <configuration>
    <argLine>-javaagent:${com.alibaba:transmittable-thread-local:jar}</argLine>
  </configuration>
</plugin>
<plugin>
  <artifactId>maven-dependency-plugin</artifactId>
  <version>3.1.2</version>
  <executions>
    <execution>
      <phase>initialize</phase>
      <goals>
        <goal>properties</goal>
      </goals>
    </execution>
  </executions>
</plugin>

可以运行工程的POM配置示例

https://github.com/oldratlee/log4j2-ttl-thread-context-map/blob/12b6b2d72d4e54da785b64a7a97970b824fd4519/pom.xml#L262-L284

因为log4j2-ttl-thread-context-map项目使用了Maven Profile,运行命令如下:

mvn test -P enable-ttl-agent-for-test

@zscgrhg 你看看

对于测试(Unit Test)的场景,上面maven-surefire-plugin配置上TTL Agent的解法 是不是简单够用了? :)

ocean-wll commented 2 years ago

我们是做压测产品,需要使用ttl对线程池中的数据进行传标。

我们想把我们的产品封装成一个jar包,可以像arthas-boot那样去动态attach agent。

对ttl的处理方式是通过手动调用 TtlAgent.premain() ,但是发现不会对已经创建了的Runnable进行增强了。

所以对这一块的处理有什么好的建议吗?


added by @oldratlee : 在Issue https://github.com/alibaba/transmittable-thread-local/issues/364#issue-1164875874 中已经回复。

kspine commented 2 years ago

当应用的字节码都加密过,agent 是否还是可用?,谢谢

oldratlee commented 2 years ago

当应用的字节码都加密过,agent 是否还是可用?,谢谢

这问题与具体某个Agent(如TTL Agent)无关。 相关的是:Java Agent 与 类文件加载 两者之间是如何协作的。

个人觉得,从类文件加载类的字节码 与 Java Agent(对加载后的类字节码做处理) 是2个独立的步骤。


@kspine 你可以拿实际碰到的场景具体运行/测试验证一下; 然后反馈一下结果 ❤️

hisenyuan commented 1 year ago

@oldratlee 请问如果要通过 runtime attach 方式启动,有什么方便的办法吗? 或者怎么拿到 ttl 对应的 java.lang.instrument.ClassFileTransformer。

目前由于某些限制,不方便使用 -javaagent 方式启动。 目前在做一个简单的 Trace,需要依赖 ttl 跨线程传递相关信息。 通过如下方式进行启动,启动过程不报错,在提交任务时会报错。

        Instrumentation instrumentation = ByteBuddyAgent.install();
        if (!TtlAgent.isTtlAgentLoaded()) {
            TtlAgent.premain(null, instrumentation);
        }
        boolean ttlAgentLoaded = TtlAgent.isTtlAgentLoaded();
        boolean enableTimerTask = TtlAgent.isEnableTimerTask();

# 报错信息
Exception in thread "main" java.lang.NoClassDefFoundError: com/alibaba/ttl/TtlRunnable
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java)