Open zscgrhg opened 4 years ago
支持运行加载agent
对于TTL是一个可以实现的功能Feature。
是否实现这个Feature,需要一起讨论一下:
对比 『启动时加载-javaagent
选项』方式 与 运行时加载方式 2者。
下面展开说明对比:
TTL Agent Jar
Agent
,即动态Attach Java Agent
参见这个java-dns-cache-manipulator
工具:
https://github.com/alibaba/java-dns-cache-manipulator/tree/master/tool这点应该是确定的。(如果不是,可以给一下资料)
Agent Jar
要准备的前提下,『启动时』加载方式 比 『运行时』加载方式 使用更简单动态Attach需要
Attach
逻辑-javaagent
选项』无需修改应用代码。参见:
TTL
改了jdk
标准类,动态加载流程更复杂,更多的可靠性风险当然,这点不是大问题,对要实现的逻辑都必须要保证好可靠性/正确性。 :)
Agent
动态加载会触发JVM JIT
退化,应用过载挂掉风险这边线上出过因为Agent
动态注入,线上应用性能劣化,结果应用过载挂掉情况。
这样的风险 也是非常大。
TTL
的Agent
运行时加载方式。 @zscgrhg欢迎大家讨论 😁
PS: 已有的issue
TTL Agent
支持运行时加载(aka. Agent Attach使用方式)的实现讨论:https://github.com/alibaba/transmittable-thread-local/issues/226#issuecomment-771538927我的场景是这样的:想实现一个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
启动参数,就不方便了。@oldratlee
可靠性和正确性的方面的考虑,是不是只要保证 依赖TTL的代码都在TTL转换完成之后执行就没有问题呢?
这点一般都是能够做到的,如果做不到,那就从premain加载就好了。
如果去掉agentmain方法,有些场景易用性就差很多了。
我的场景是这样的:想实现一个
JUnit Rule
,以简化单元测试的开发。比如我有一个Service: ...
@zscgrhg 关于使用TTL Agent
的单元测试开发简化,想到下面的一种解决方法:
在Maven
运行单元测试时,配置好TTL Agent
的JVM
参数。
<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
配置示例因为log4j2-ttl-thread-context-map
项目使用了Maven Profile
,运行命令如下:
mvn test -P enable-ttl-agent-for-test
@zscgrhg 你看看
Unit Test
)的场景,上面maven-surefire-plugin
配置上TTL Agent
的解法 是不是简单够用了? :)我们是做压测产品,需要使用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 中已经回复。
当应用的字节码都加密过,agent 是否还是可用?,谢谢
当应用的字节码都加密过,agent 是否还是可用?,谢谢
这问题与具体某个Agent
(如TTL Agent
)无关。
相关的是:Java Agent
与 类文件加载 两者之间是如何协作的。
个人觉得,从类文件加载类的字节码 与 Java Agent
(对加载后的类字节码做处理)
是2个独立的步骤。
@kspine 你可以拿实际碰到的场景具体运行/测试验证一下; 然后反馈一下结果 ❤️
@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)
-javaagent:PremainAgent.jar