导读

  • SkyWalking 是基于 javaagent 的两大字节码操作工具之一的 Byte Buddy 实现的无侵入 APM(application performance monitor) 系统,目前项目在 Apache 孵化器中,想了解 SkyWalking 和 Byte Buddy 源码的同学可在文章底部参考链接中,跳转至对应的官方资源。
  • 本文会已通过 Byte Buddy 实现应用组件 SpringMVC 记录请求路径、入参、执行时间的 javagent 项目、持续迭代 javaagent 项目的方法论、SkyWalking agent 在项目如何持续 debug 插件代码、以及 SkyWalking 插件开发实践的四个章节,让大家掌握 SkyWalking 的玩法,进而让 SkyWalking 在自己公司中的二次开发变得触手可及。

Byte Buddy 实现

  • 首先如果你对 javaagent 还不是很了解可以先百度一下,或在公众号内看下《JavaAgent 原理与实践》简单入门下。SpringMVC 分发请求的关键方法相信已经不用我在赘述了,让我们代码代码一把唆吧。
  • 编写 Byte Buddy javaagent 代码
 public class AgentMain {
    public static void premain(String agentOps, Instrumentation instrumentation) {
        new AgentBuilder.Default()
                .type(ElementMatchers.named("org.springframework.web.servlet.DispatcherServlet"))
                .transform((builder, type, classLoader, module) ->
                        builder.method(ElementMatchers.named("doDispatch"))
                                .intercept(MethodDelegation.to(DoDispatchInterceptor.class)))
                .installOn(instrumentation);
    }
}
复制代码
  • 编写 DispatcherServlet doDispatch 拦截器代码(是不是跟 AOP 如出一辙)
 public class DoDispatchInterceptor {
    @RuntimeType
    public static Object intercept(@Argument(0) HttpServletRequest request, @SuperCall Callable<?> callable) {
        final StringBuilder in = new StringBuilder();
        if (request.getParameterMap() != null && request.getParameterMap().size() > 0) {
            request.getParameterMap().keySet().forEach(key -> in.append("key=" + key + "_value=" + request.getParameter(key) + ","));
        }
        long agentStart = System.currentTimeMillis();
        try {
            return callable.call();
        } catch (Exception e) {
            System.out.println("Exception :" + e.getMessage());
            return null;
        } finally {
            System.out.println("path:" + request.getRequestURI() + "入参:" + in + "耗时:" + (System.currentTimeMillis() - agentStart));
        }
    }
}
复制代码
  • 增加 agent 描述文件 resources.META-INF.MANIFEST.MF
Manifest-Version: 1.0
Premain-Class: com.z.test.agent.AgentMain
Can-Redefine-Classes: true
复制代码
  • pom.xml 文件
dependencies
    +net.bytebuddy.byte-buddy 
    +javax.servlet.javax.servlet-api *scope=provided
plugins
    +maven-jar-plugin *manifestFile=src/main/resources/META-INF/MANIFEST.MF
    +maven-shade-plugin *include:net.bytebuddy:byte-buddy🏺
    +maven-compiler-plugin
复制代码
  • 小结:没几十行代码就完成了通过 Byte Buddy 实现应用组件 SpringMVC 记录请求路径、入参、执行时间 javagent 项目,是不是觉得自己很优秀。

持续迭代 javaagent

  • 本小结主要介绍 javaagen 如何 debug,以及持续集成。
  • 首先我的 javajagent 项目目录结构如图所示:
  • 应用项目是用几行代码实现的 SpringBootWeb 项目:
@SpringBootApplication(scanBasePackages = {"com"})
public class TestBootWeb {
    public static void main(String[] args) {
        SpringApplication.run(TestBootWeb.class, args);
    }
    @RestController
    public class ApiController {
        @PostMapping("/ping")
        public String ping(HttpServletRequest request) {
            return "pong";
        }
    }
}
复制代码
  • 下面是关键 javaagent 项目如何持续迭代与集成:
VM options增加:-javaagent:/Users/zhao/Code/github/z_my_test/test-agent/target/test-agent-1.0-SNAPSHOT.jar=aaaaa
Before launch 在Build之前增加:
    Working directory:/Users/zhao/Code/github/incubator-skywalking
    Command line:-T 1C -pl test-agent -am clean package -Denforcer.skip=true -Dmaven.test.skip=true -Dmaven.compile.fork=true
复制代码
  • 详细配置见图片:
  • 小结: 看到这里的将 javaagent 持续迭代集成,是不是瞬间觉得自己手心已经发痒起来,很想唆一个自己的 agent 代码了呢,等等还有一个好消息:test-demo 这 10 几行的代码实现的 Web 服务居然有 5k 左右的类可以使用 agent 增强,根据二八原则,一般的程序员至少熟悉 1k 左右的类,还不挑一个自己熟悉的类去挑战下?
  • 注意 mvn 编译加速的命令是 maven3+ 版本以上才支持的哈。

SkyWalking Debug

  • 峰回路转,到了文章的主题 SkyWalking 之高级玩法的正文啦,其实通过了上面的铺垫,我想大家也或多或少已经知道我要说怎么 SkyWalking 怎么 Debug 了。所以我这里主要讲几个可以优化点,避免大家觉得没有新意,提前卖个关子,我的集成时间优化到 30 秒左右哈:
VM options增加:-javaagent:-javaagent:/Users/zhao/Code/github/incubator-skywalking/skywalking-agent/skywalking-agent.jar:不要用dist里面的skywalking-agent.jar,具体原因大家可以看看源码^_^
Before launch 在Build之前增加:
    Working directory:/Users/zhao/Code/github/incubator-skywalking
    Command line:-T 1C -pl apm-sniffer/apm-sdk-plugin -amd clean package -Denforcer.skip=true -Dmaven.test.skip=true -Dmaven.compile.fork=true: 这里我针对插件包,因为紧接着下文要开发插件
另外根pom注释maven-checkstyle-plugin也可加速编译
复制代码
  • javaagent 项目想 debug,还需要将 agent 代码与接入 agent 项目至少在同一个工作空间内,网上方法有很多,这里我推荐大家一个最简单的方法。File->New->Module from Exisiting Sources... 引入 skywalking-agent 源码即可

kob 之 SkyWalking 插件编写

  • kob(贝壳分布式作业调度框架)是贝壳找房项目微服务集群中的基础组件,通过编写贝壳分布式作业调度框架的 SkyWalking 插件,可以实时收集作业调度任务的执行链路信息,从而及时得到基础组件的稳定性,了解细节可点击阅读《贝壳分布式调度框架简介》。想详细了解 SkyWalking 插件编写可在文章底部参考链接中,跳转至对应的官方资源,好话不多说,代码一把唆起来。
  • apm-sdk-plugin pom.xml 增加自己的插件 model
<artifactId>apm-sdk-plugin</artifactId>
    <modules>
        <module>kob-plugin</module>
        ...
    <modules>
复制代码
  • resources.skywalking-plugin.def 增加自己的描述
kob=org.apache.skywalking.apm.plugin.kob.KobInstrumentation
复制代码
  • 编写方法 instrumentation
public class KobInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
    private static final String ENHANCE_CLASS = "com.ke.kob.client.spring.core.TaskDispatcher";
    private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.kob.KobInterceptor";
    @Override
    protected ClassMatch enhanceClass() {
        return NameMatch.byName(ENHANCE_CLASS);
    }
    @Override
    protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return null;
    }
    @Override
    protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
                new InstanceMethodsInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return named("dispatcher1");
                    }
                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPT_CLASS;
                    }
                    @Override
                    public boolean isOverrideArgs() {
                        return false;
                    }
                }
        };
    }
}
复制代码
  • 自定义 interceptor 实现 span 的创建
public class KobInterceptor implements InstanceMethodsAroundInterceptor {
    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments,  Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
        final ContextCarrier contextCarrier = new ContextCarrier();
        com.ke.kob.client.spring.model.TaskContext context = (TaskContext) allArguments[0];
        CarrierItem next = contextCarrier.items();
        while (next.hasNext()) {
            next = next.next();
            next.setHeadValue(JSON.toJSONString(context.getUserParam()));
        }
        AbstractSpan span = ContextManager.createEntrySpan("client:"+allArguments[1]+",task:"+context.getTaskKey(), contextCarrier);
        span.setComponent(ComponentsDefine.TRANSPORT_CLIENT);
        SpanLayer.asRPCFramework(span);
    }
    @Override
    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
        ContextManager.stopSpan();
        return ret;
    }
    @Override
    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
    }
}
复制代码
  • 实现效果,将操作名改成任务执行节点 + 任务执行方法,实现 kob 的 SkyWalking 的插件编写,加上报警体系,可以进一步增加公司基础组件的稳定性。

参考链接

  • Java

    Java,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台的总称。用 Java 实现的 HotJava 浏览器(支持 Java applet)显示了 Java 的魅力:跨平台、动态的…

    380 引用 • 6 回帖
感谢    赞同    分享    收藏    关注    反对    举报    ...