使用ByteBuddy实现OpenTracing时如何避免NoClassDefFoundError

时间:2019-07-08 12:42:57

标签: java byte-buddy opentracing

我正在尝试实现一个Java代理,该代理可以轻松添加到我们一些旧服务的命令行中,以使用 OpenTracing API 启用分布式跟踪。我正在使用ByteBuddy(1.9.12),并在到目前为止针对以下情况运行了检测代码时,点击了NoClassDefFoundErrors

  1. java.util.concurrent包中插入了一个类:找不到GlobalTracer,因为Java Agent仅在应用程序类路径中。我通过在命令行(仅Java 8)上将包含跟踪API的Java代理添加到引导类路径中来暂时解决此问题,但我希望能够以编程方式进行此操作。寻找正确的方法。 (编辑:这是通过使用Java Agent清单文件中的Boot-Class-Path选项解决的,只需将Java Agent JAR本身的文件名(没有路径)放在文件中即可。)
  2. 插入了一个自定义网络客户端类:由于我的Intercept类不在Java代理JAR中,因此无法从我的Intercept类的自定义网络软件包中找到一个类(在字节代码注入期间)。我正在寻找一种“正确的方法”来高层地组织事物以避免这种情况。

方案2的代码

public class NettyAgentRule implements AgentRule {

    public Iterable<? extends AgentBuilder> buildAgent(AgentBuilder agentBuilder) {
        return Arrays.asList(agentBuilder
                .type(hasSuperType(named("org.jboss.netty.bootstrap.Bootstrap")))
                .transform((builder, typeDescription, classLoader, module) -> {
                    return builder.visit(Advice.to(NettyAgentRule.class).on(named("setPipelineFactory")));
                }));
    }

    @Advice.OnMethodEnter
    public static void enter(final @Advice.Origin String origin,
                            final @Advice.This Object thiz,
                            @Advice.Argument(value = 0, readOnly = false, typing = Assigner.Typing.DYNAMIC) Object parameter) {
        parameter = NettyAgentIntercept.enter(thiz, parameter);
    }
}

在引用Netty类的NettyAgentIntercept类中发生错误的地方。

我研究了OpenTracing'contrib'SpecialAgent如何处理这些情况,并且正在进行大量自定义类加载,这与它们的构建结构(遵循命名约定的JAR中的JAR)紧密相关。如果可能的话,这将是最好的避免方法。

场景2中的异常堆栈示例

java.lang.NoClassDefFoundError: org/jboss/netty/channel/ChannelPipelineFactory
2019-07-05 18:52:21,10613       at com.example.tracing.netty.NettyAgentIntercept.enter(NettyAgentIntercept.java:9)
2019-07-05 18:52:21,10613       at org.jboss.netty.bootstrap.Bootstrap.setPipelineFactory(Bootstrap.java:251)
2019-07-05 18:52:21,10616       at com.example.lib.util.net.NettyCore.<init>(NettyCore.java:176)
2019-07-05 18:52:21,10616       at com.example.lib.util.net.NetClient.getInstance(NetClient.java:125)
2019-07-05 18:52:21,10617       at com.example.lib.util.net.NetClient.getInstance(NetClient.java:147)
2019-07-05 18:52:21,10617       at com.example.lib.util.net.NetClient.getInstance(NetClient.java:55)
2019-07-05 18:52:21,10617       at com.example.component.Component.main(Component.java:155)
2019-07-05 18:52:21,10618       at com.example.lib.util.invoke.ComponentThread.run(ComponentThread.java:21)

我还尝试了以下推荐的代码

byte-buddy throws java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet

但是会导致同样的异常:

public Iterable<? extends AgentBuilder> buildAgent(AgentBuilder agentBuilder) {
        return Arrays.asList(agentBuilder
                .type(hasSuperType(named("org.jboss.netty.bootstrap.Bootstrap")))
                .transform(new AgentBuilder.Transformer.ForAdvice()
                        .include(getClass().getClassLoader())
                        .advice(named("setPipelineFactory"),"com.example.tracing.netty.NettyAgentAdvice")
                ));

    }

建议类别为

的地方
package com.example.tracing.netty;

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;

import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bytecode.assign.Assigner;

public class NettyAgentAdvice {

    @Advice.OnMethodEnter
    public static void enter(final @Advice.Origin String origin,
                            final @Advice.This Object thiz,
                            @Advice.Argument(value = 0, readOnly = false, typing = Assigner.Typing.DYNAMIC) Object parameter) {
        parameter = enter(thiz, parameter);
    }

    public static ChannelPipelineFactory enter(Object thiz, Object returned) {
        ChannelPipelineFactory pipelineFactory = (ChannelPipelineFactory) returned;
        if (thiz instanceof ClientBootstrap) {
            return () -> {
                ChannelPipeline pipeline = pipelineFactory.getPipeline();
                if (pipeline.get(TracingHandler.class) == null) {
                    pipeline.addLast("tracing", new TracingHandler());
                }
                return pipeline;
            };
        }
        return pipelineFactory;
    }
}

1 个答案:

答案 0 :(得分:0)

Byte Buddy为代理生成器提供了一个建议适配器,该适配器避免了类的加载和反射,从而避免了您看到的错误:AgentBuilder.Transformer.ForAdvice。

此转换器将使用TypePool来解析目标类加载器和您添加的代表类的任何类加载器中的建议类。