分布式调用链(二)---Control/Service层插桩埋点实际处理

为什么要在Control/Service 层进行埋点

因为请求过来以后,先进入Control,在进入Service。

在Control:可以统计某个URL,具体的执行次数、时间、一段时间的流量统计。

在Service :可以统计某个方法的用时。

当有了数据以后,可以使用kibana来做可视化工具。kibana是属于elasticsearch的一款工具。 

采集端执行流程

应用系统嵌入监听器,采集到数据以后,由http协议发送给集群,

在发送给elasticsearch.(也可以直接进入DB,但是不灵活),

elasticsearch好处: 当遇到异常信息以后,可以用其反向搜索引擎,

采集流程

  • 判定谁是采集目标
  • 构建插桩后的Class字节
  • 采集方法运行时信息,如开始时间、结束时间、方法名
  • 上传运行时信息

 怎么知道某个类是需要采集的类? 

 怎么知道是Control还是service

根据UML图,可以将共同点抽象出来,如所有类都要判定是否是目标,都要加载到ClassLoader。所有抽象出两个接口:

 

但不是所有采集器不一定要记录开始信息、结束信息、异常信息、统计上传信息,只是一般情况会。所以可以抽象出一个抽象类。AbstractCollects。在上传信息的时候,需要用到线程,自定义一个threadService.

总之做设计时候,必须用到的用接口,通用的用抽象类。接口是用来简化编程,屏蔽底层实现。

 采集器需要一个入口,定义AgentMain

 具体定义采集器,由于需要采集Controler、service、jdbc层的信息,定义3个采集器。如果用到Dubbo,可以继续定义。

将采集器注册到AgentMain中,由于采集器不多,后期扩展欲望不是很强,可以采取硬编码方式,也可以写个xml文件进行配置,方便插拔。

Service层的采集

//判定是否是service层代码
public boolean isTarget(String className, ClassLoader loader, CtClass ctclass) {
        try {
            //获取class类的注解,然后遍历。如果注解是@service,则命中。
            for (Object obj : ctclass.getAnnotations()) {
                if (obj.toString().startsWith("@org.springframework.stereotype.Service")) {
                    return true;
                }
            }
        } catch (ClassNotFoundException e) {
            System.err.println(String.format("bit apm run error targetClassName=%s errorMessage=%s",className,e.getClass().getSimpleName()+":"+e.getMessage()));
        }
        return false;
    }
public byte[] transform(ClassLoader loader, String className, byte[] classfileBuffer, CtClass ctclass) throws
            Exception {
        AgentLoader byteLoade = new AgentLoader(className, loader, ctclass);

        CtMethod[] methods = ctclass.getDeclaredMethods();
        for (CtMethod m : methods) {
            //排除写法,比火箭写法好,可读性更高。火箭写法作者逻辑清楚,但不利于可读性。
            // 屏蔽非公共方法
            if (!Modifier.isPublic(m.getModifiers())) {
                continue;
            }
            // 屏蔽静态方法
            if (Modifier.isStatic(m.getModifiers())) {
                continue;
            }
            // 屏蔽本地方法
            if (Modifier.isNative(m.getModifiers())) {
                continue;
            }

            AgentLoader.MethodSrcBuild build = new AgentLoader.MethodSrcBuild();
            build.setBeginSrc(String.format(beginSrc,className,m.getName()));
            build.setEndSrc(endSrc);
            build.setErrorSrc(errorSrc);
            byteLoade.updateMethod(m, build);
        }
        return byteLoade.toBytecote();
    }

由于封装的时候,涉及到执行前,执行后,与出异常3种情况,所以定义三个方法。

猜你喜欢

转载自blog.csdn.net/Damon__Wang/article/details/82051631