欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-18 11:19:37
...

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

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

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

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

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

采集端执行流程

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

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

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

 

采集流程

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

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

 怎么知道是Control还是service

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

 

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

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

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

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

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

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

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

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

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

 

将采集器注册到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种情况,所以定义三个方法。

相关标签: 调用链 javassist