文章目录

  • 前言
  • 动手实现apm上报功能
    • 代码
    • 检验结果
    • 总结
  • 等待优化点
  • 项目链接

前言

最近刚刚换了工作,进了公司的架构组,有些项目蛮有意思,也是很感兴趣,也会工作之余自行学习,比如说有个自研apm项目

当然在此声明一下,本篇代码属于个人学习编写,并非copy公司代码

对于springcloud,有一套sleuth(主要是traceId+spanId生成)+zipkin(数据统计功能)
skywalking,淘宝的鹰眼,蚂蚁金服sofatrace等等

动手实现apm上报功能

  1. 首先agent用的是byte-buddy
  2. 定义一个context方便储存traceid,spanid以及上报的数据
  3. 只是一个比较简陋的demo,需要后续一些功能优化

代码

编写一个agent

package com.example.demo.agent;import com.example.demo.interceptor.MyInterceptor;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;import java.lang.instrument.Instrumentation;public class MyAgent {public static final ThreadLocal<TraceContext> LOCAL = new ThreadLocal<>();public static void premain(String agentArgs, Instrumentation inst) {System.out.println("this is an perform monitor agent.");AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader) -> {return builder.method(ElementMatchers.any()) // 拦截任意方法.intercept(MethodDelegation.to(MyInterceptor.class)); // 委托};AgentBuilder.Listener listener = new AgentBuilder.Listener() {@Overridepublic void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, DynamicType dynamicType) {}@Overridepublic void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {}@Overridepublic void onError(String typeName, ClassLoader classLoader, JavaModule module, Throwable throwable) {}@Overridepublic void onComplete(String typeName, ClassLoader classLoader, JavaModule module) {}};new AgentBuilder.Default().type(ElementMatchers.nameStartsWith("com.example.demo").and(ElementMatchers.not(ElementMatchers.nameStartsWith("com.example.demo.agent"))))// 指定需要拦截的类.transform(transformer).with(listener).installOn(inst);}
}

然后写下agent拦截下这些类之后需要做的操作

package com.example.demo.interceptor;import com.alibaba.fastjson.JSON;
import com.example.demo.agent.TraceContext;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;import java.lang.reflect.Method;
import java.util.concurrent.Callable;import static com.example.demo.agent.MyAgent.LOCAL;public class MyInterceptor {@RuntimeTypepublic static Object intercept(@Origin Method method,@SuperCall Callable<?> callable) throws Exception {long start = System.currentTimeMillis();try {// 原有函数执行return callable.call();} finally {TraceContext context = LOCAL.get();if(context != null){context.setMethodType(method.getDeclaringClass().getName()+"."+method.getName());context.setTime("调用方法时间:"+ (System.currentTimeMillis() - start) +"ms");//上报操作,rpc,这里还需要修改sql等等打印到logback也上报到收集中心System.out.println(JSON.toJSONString(context));context.clear();}}}}

看下自定义上下文context

package com.example.demo.agent;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.util.concurrent.atomic.AtomicInteger;/*** @author M*/
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TraceContext implements Serializable {private String traceId;private String spanId;private String parentSpaceId;private String time;private String methodType;private String data;//spanId separatorpublic static final String RPC_ID_SEPARATOR = ".";/*** sub-context counter*/private static AtomicInteger childContextIndex = new AtomicInteger(0);/*** 如果rpc调用的时候需要将spanid传递成这个方法的值** @return*/public String nextChildContextId() {return this.spanId + RPC_ID_SEPARATOR + childContextIndex.incrementAndGet();}public static TraceContext cloneContext(TraceContext context) {if(context==null){return new TraceContext();}return TraceContext.builder().spanId(context.nextChildContextId()).parentSpaceId(context.getSpanId()).traceId(context.getTraceId()).build();}public void clear() {//上报之后需要清理之前的一些数据this.data = "";}
}

生成agent jar包

这个自行百度,启动的时候加上 -javaagent:D:\github\agent-apm\out\artifacts\MyAgent\MyAgent.jar

单元测试

package com.example.demo.test;import com.alibaba.fastjson.JSON;
import com.example.demo.agent.TraceContext;import java.util.UUID;import static com.example.demo.agent.MyAgent.LOCAL;public class AgentTest {private void fun1() throws Exception {TraceContext context = LOCAL.get();if (context != null) {//由于没有集成sleuth,spaceId需要自己模调用的时候简单的自增String spaceId = context.getSpanId();//rpc调用的时候需要+1context.setSpanId(spaceId);context.setParentSpaceId(spaceId);context.setData("fun1需要上报的数据");}System.out.println("this is fun 1.");Thread.sleep(500);}private void fun2() throws Exception {TraceContext context = LOCAL.get();if (context != null) {//由于没有集成sleuth,spaceId需要自己模调用的时候简单的自增String spaceId = context.getSpanId();//rpc调用的时候需要+1context.setSpanId(spaceId);context.setParentSpaceId(spaceId);if (!"".equals(context.getData())) {System.out.println("fun2可以拿到之前context上传数据:" + context.getData());}}System.out.println("this is fun 2.");Thread.sleep(500);//模拟调用rpcTraceContext rpcContext = TraceContext.cloneContext(context);System.out.println("上报rpc context:" + JSON.toJSONString(rpcContext));}/*** 可以重写logback append逻辑,打印日志也上报到收集数据的系统** @param args* @throws Exception*/public static void main(String[] args) throws Exception {//实际开发由sleuth来生成traceIdString traceId = UUID.randomUUID().toString();String spaceId = "0";TraceContext context = TraceContext.builder().spanId(spaceId).parentSpaceId("0").traceId(traceId).build();//如果是rpc的话,需要使用拦截器,将context塞到LOCAL里面LOCAL.set(context);AgentTest test = new AgentTest();test.fun1();test.fun2();//实际开发需要拦截器去删除本地变量LOCAL.remove();}
}

打印结果

this is an perform monitor agent.
this is fun 1.
{"data":"fun1需要上报的数据","methodType":"com.example.demo.test.AgentTest.fun1","parentSpaceId":"0","spanId":"0","time":"调用方法时间:503ms","traceId":"add0ac18-9918-4c59-846d-a04802000bae"}
this is fun 2.
上报rpc context:{"parentSpaceId":"0","spanId":"0.1","traceId":"add0ac18-9918-4c59-846d-a04802000bae"}
{"data":"","methodType":"com.example.demo.test.AgentTest.fun2","parentSpaceId":"0","spanId":"0","time":"调用方法时间:507ms","traceId":"add0ac18-9918-4c59-846d-a04802000bae"}

检验结果

有traceid、spanid可以构建一条调用链路,其次的话会打印方法名,执行时间,可以进行后续相应的代码优化,以及加上了需要自定义上报的数据

总结

我们可以看到打印结果,可以看出同一个traceId,以及同一个应用spanid也是一样的,如果说跨应用,spanid需要重新设置,然后进行传递

spanId生成规则

参考下sofatrace 生成规则

就是上面的0.1,0.1.1 spanId

等待优化点

  1. rpc部分需要重新,从header头拿到context赋值到threadlocal
  2. mysql打印sql以及执行时间,也需要重写
  3. 重写logback append逻辑,我们平时打印的日志也需要上报的数据中心

项目链接

github:https://github.com/dajitui/agent-apm

agent实现apm上报相关推荐

  1. SkyWalking Agent数据采集和上报原理浅析

    文章目录 整体工作原理 采集数据过程分析 领域建模 ids trace tag context 数据生产 数据消费 具体插件分析 总结 Skywalking是一款云原生的APM(应用性能管理)系统.包 ...

  2. 基于Java Agent实现APM

    一.APM概述 APM系统(Application Performance Management,即应用性能管理),用于对应用系统做实时监控,目的是实现对应用性能管理和故障定位. 1.1.为什么需要A ...

  3. 多语言应用性能监控系统:Elastic APM

    点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 概述 Elastic APM 是基于 Elastic Stack 构建的应用性能监控系统.通 ...

  4. 海量监控数据处理之道(一):APM指标计算优化

    作者:熊彪,腾讯云监控高级工程师 前言 腾讯云应用性能观测(APM)是一款应用性能管理产品,基于实时的多语言应用探针全量采集技术,为用户提供分布式应用性能分析和故障自检能力.本文主要讲述了 APM 链 ...

  5. Java Agent与Native Agent的利与弊

    原文:http://apmblog.dynatrace.com/2014/01/15/pros-and-cons-of-using-java-vs-native-agent-for-applicati ...

  6. 应用性能监控-- SkyWalking

    应用性能监控-- SkyWalking 文章目录 应用性能监控-- SkyWalking APM 与 SkyWalking SkyWalking 与 Sleuth + Zipkin 比较 SkyWal ...

  7. SkyWalking环境搭建(elasticsearch7)

    主要有部分内容 oap服务:接收和保存采集的数据,同时个UI展示 SkyWalking Website:展示oap服务的数据 SkyWalking agent:采集应用数据 下载安装包 下载地址:ht ...

  8. 调用链追踪系统在伴鱼:理论篇

    本文将调用链追踪系统的设计维度归结于以下 5 个:调用链数据模型.元数据结构.因果关系.采样策略以及数据可视化.我们可以把这 5 个维度当作一个分析框架,用它帮助我们在理论上解构市面上任意一个调用链追 ...

  9. 全链路压测体系建设方案的思考与实践

    在阿里淘宝 双11 的过程中,长期以来都是在生产环节做全链路压测的,通过实践我们发现在生产环境中做压测,实际上会和一个 IT 组织的结构.成熟度.流程等紧密相关,所以我们把全链路压测从简单的制作范围内 ...

  10. 一杯茶的时间,上手Zabbix

    " IT 运维离不开系统监控,就好像鱼儿离不开水一样.一款强大的监控系统可以有力保证设备和业务的稳定. 来自:51cto技术栈 在监控系统层出不穷的今天,作为老牌监控系统的 Zabbix 依 ...

最新文章

  1. Kali Linux攻防系统(一:攻防系统Kali Linux下载安装与更新)
  2. HTML meta标签总结
  3. 转载——Java与WCF交互(一):Java客户端调用WCF服务
  4. FLASH模仿苹果菜单源码.(AS苹果菜单源码)
  5. 任务间通信的基本知识
  6. 记一次T-SQL查询优化 索引的重要性
  7. centos内核参数调优
  8. EFI BIOS下的磁盘管理工具Diskpart,Efifmt与Efichk(转)
  9. POJ2190 HDU2714 ISBN
  10. (整理)C/C++野指针
  11. android gps转换度分秒,如何将GPS数据转换为度分秒
  12. linux mint 8812ac网卡,kali虚拟机连接无线网卡comfast cf-812AC(Realtek 8812BU芯片)(不支持wlan0mon)...
  13. 国家软考--高级系统分析师证书
  14. 常用网络拓扑在网络规划设计中
  15. 番外2. OpenCV 中摄像头捕获与视频处理与常见问题解决方案
  16. 手机端访问本地编写的html页面【亲测有效】
  17. Vue3 全家桶实践
  18. 账号密码的注册、登录、修改和注销
  19. win10开机占用内存大,资源管理器也看不到运行进程
  20. Vue学习笔记-项目开发2.5图文列表展示(周末去哪玩儿)

热门文章

  1. win10隐藏登入界面时的administrator账户
  2. python turtle渐变色_Python : turtle色彩控制实例详解
  3. Nexmoe 博客主题的设计理念
  4. windows网络和共享中心“查看基本网络信息并设置连接”为“未知”的解决方案
  5. VRay(一)材质基础
  6. 如何理解TPS、QPS、RT、吞吐量这些性能指标
  7. 二极管ROM的工作原理解读
  8. 分辨率单位及换算,LW / PH、LP / mm、L / mm、Cycles / mm、Cycles / pixel、LP / PH
  9. cmd如何返回上一级目录,如何进入其他文件目录
  10. ​​insecure-configuration --复现