Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
zscgrhg opened this issue Mar 31, 2020 · 8 comments
Open

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

zscgrhg opened this issue Mar 31, 2020 · 8 comments

Comments

@zscgrhg
Copy link

zscgrhg commented Mar 31, 2020

  • 不想使用启动参数-javaagent:PremainAgent.jar
  • 想在main方法里加载它
@zscgrhg zscgrhg changed the title 能支持下运行时加载agent吗? 大佬,请支持下运行时加载agent,谢谢 Mar 31, 2020
@oldratlee oldratlee changed the title 大佬,请支持下运行时加载agent,谢谢 支持运行加载agent Apr 2, 2020
@oldratlee oldratlee added ❓question Further information is requested ✨ feature labels Apr 2, 2020
@oldratlee
Copy link
Member

oldratlee commented Apr 2, 2020

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

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

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

下面展开说明对比:

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

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

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

动态Attach需要

  • 实现Attach逻辑
  • 做Attach操作
    • 一次Java命令来Attach
    • 或是 进程自己Attach自己,这个意味要改应用代码,『启动时加载-javaagent选项』无需修改应用代码。

参见:

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

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

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

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


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

欢迎大家讨论 😁

PS: 已有的issue

@oldratlee oldratlee changed the title 支持运行加载agent 支持运行时加载TTL agent Apr 2, 2020
@zscgrhg
Copy link
Author

zscgrhg commented Apr 5, 2020

我的场景是这样的:想实现一个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();
        }
    }
}

这种场景下,

  • 需要能够在Rule里面加载agent
  • 这样我的原始用例只需要引入@Rule ZUnitWatcher就行了,集成方式比较友好。
  • 如果还需要设置jvm启动参数,就不方便了。

@zscgrhg
Copy link
Author

zscgrhg commented Apr 7, 2020

@oldratlee

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

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

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

@oldratlee
Copy link
Member

oldratlee commented Sep 3, 2020

我的场景是这样的:想实现一个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
Copy link

ocean-wll commented Dec 21, 2021

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

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

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

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


added by @oldratlee : 在Issue #364 (comment) 中已经回复。

@kspine
Copy link

kspine commented Aug 7, 2022

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

@oldratlee
Copy link
Member

oldratlee commented Aug 7, 2022

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

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

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


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

@hisenyuan
Copy link

hisenyuan commented Jun 5, 2023

@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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants