点击上方蓝色“方志朋”,选择“设为星标”

回复“666”获取独家整理的学习资料!

转自:尹吉欢 / 猿天地

想要在程序里监控数据库的操作耗时,想要在底层框架中自动传递链路跟踪信息,这些需求经常会碰到,特别是在构建基础框架的时候。

核心目标只有一个,那就是在底层封装好,不用上层使用人员关心。今天跟大家聊聊常用的底层扩展埋点方式是怎么处理的。

框架自带扩展点

如果你使用的框架在设计的时候,就预留了扩展点就很方便了。比如 Mybatis 的拦截器,我们可以在拦截器中对 SQL 进行监控,改写。

比如阿里的 Sentinel 框架,可以通过 SPI 来扩展 Slot,调整编排顺序,新增自定义的 Slot 来实现限流告警等。

开源框架的质量参差不齐,有在早期设计比较好的,留足了各种扩展点,方便使用者。也有一些没有考虑那么全面,导致你在使用的时候需要进行扩展,发现找不到扩展点,对于框架本身没有提供扩展点的场景,请接着看下面。

修改源码

如果框架没有扩展点,最直接的方式就是修改开源框架的源码来扩展自己想要的功能,通常的做法就是克隆源码到自己的私有仓库中,然后修改、测试、重新打包使用。

像我们之前用了 XXL-JOB 做任务调度,也是修改了某些代码,在界面上扩展了监控通知的配置信息,默认是只支持邮箱,可以扩展出手机、钉钉等。

修改源码不好的点在于需要跟原框架的版本进行对齐,如果不对齐,随便改都没事。不对齐意味着修复了某些 bug 和新增了某些功能,就无法使用了。要对齐,就需要不断的将新版本的代码合并到你自己的分支上。

还有很多公司,就是基于开源的版本,构建了公司内部自己的版本,后续直接就是跟着内部的使用需求去扩展和维护了,不会跟社区的版本进行同步,如果你们有专门的团队去做这件事情,也是一种方式。

同名文件覆盖

改源码的方式需要经常同步新版本的代码,有的时候往往只想修改某一个类而已,比如对底层的某些操作进行埋点监控,如果框架本身没有提供扩展点的话只能改源码来实现。

其实还有个投机取巧的方式,就是在项目中创建一个跟你要修改的一模一样的类,包名+类目都一样,方法名也一样,方法的实现你可以改。这样就能覆盖 jar 包中的类了,还是跟类加载顺序有关系,先加载你自己定义的。

这样的方式好处在于不用经常去同步新版本的代码,如果你用的框架版本升级了,只要包名和类名不变,你这个覆盖的只是那个类而已,新增的功能和修复的 bug 都不会有影响。

切面拦截

切面在做很多统一处理的时候非常有用,同样在做底层埋点的场景也适用。

比如我们要对项目中 MongoDB 的所有操作都进行埋点监控,可以修改 MongoDB 的驱动源码,可以创建同名文件进行覆盖,方式有很多种。找到一个合适,又能实现需求的最重要。

以 Spring 中操作 MongoDB 来说明,在 Spring Data MongoDB 中会 MongoTemplate 来操作 MongoDB。最简单的方式就是直接对 MongoTemplate 类进行埋点,这样所有的操作都可以监控起来。

用切面直接切到 MongoTemplate 的所有方法上,然后进行埋点,就很简单了。

@Aspect
public class MongoTemplateAspect {@Pointcut("execution(* org.springframework.data.mongodb.core.MongoTemplate.*(..))")public void pointcut() {}@Around("pointcut()")public Object around(final ProceedingJoinPoint pjp) throws Throwable {String callMethod = pjp.getSignature().getDeclaringType().getSimpleName() + "." + pjp.getSignature().getName();Map<String, Object> data = new HashMap<>();data.put("params", JsonUtils.toJson(pjp.getArgs()));return CatTransactionManager.newTransaction(() -> {try {return pjp.proceed();} catch (Throwable throwable) {throw new RuntimeException(throwable);}}, "Mongo", callMethod, data);}
}

又比如,你还想监控 Redis 相关的,Redis 用的也是跟 Spring 整合的框架,那么也有 RedisTemplate 这个类,同样也可以用切面来实现。

基于 Template 类来埋点,相对比较上层,如果还想在底层一点进行监控,也就是 Connection 这层,Template 里面的操作都是基于 Connection 来实现的。

同样我们可以用切面来替换 Connection 相关的实现,比如可以用切面切到获取 Connection 的方法,然后替换 Connection 的对象为具备埋点监控的对象。

@Aspect
public class RedisAspect {@Pointcut("target(org.springframework.data.redis.connection.RedisConnectionFactory)")public void connectionFactory() {}@Pointcut("execution(org.springframework.data.redis.connection.RedisConnection *.getConnection(..))")public void getConnection() {}@Pointcut("execution(org.springframework.data.redis.connection.RedisClusterConnection *.getClusterConnection(..))")public void getClusterConnection() {}@Around("getConnection() && connectionFactory()")public Object aroundGetConnection(final ProceedingJoinPoint pjp) throws Throwable {RedisConnection connection = (RedisConnection) pjp.proceed();return new CatMonitorRedisConnection(connection);}@Around("getClusterConnection() && connectionFactory()")public Object aroundGetClusterConnection(final ProceedingJoinPoint pjp) throws Throwable {RedisClusterConnection clusterConnection = (RedisClusterConnection) pjp.proceed();return new CatMonitorRedisClusterConnection(clusterConnection);}
}

CatMonitorRedisConnection 中对原生的 RedisConnection 做了增强,也不会影响原有的 RedisConnection 的功能。

public class CatMonitorRedisConnection implements RedisConnection {private final RedisConnection connection;private CatMonitorHelper catMonitorHelper;public CatMonitorRedisConnection(RedisConnection connection) {this.connection = connection;this.catMonitorHelper = new CatMonitorHelper();}@Overridepublic byte[] get(byte[] key) {return catMonitorHelper.execute(RedisCommand.GET, key, () -> connection.get(key));}
}

Java Agent

Java Agent 可以在运行期将已经加载的类的字节码进行变更,可以加入我们需要进行监控的代码逻辑。无需对原有代码进行改造,没有侵入性。

在非常多优秀的开源框架中都看到了 Java Agent 的应用,像 APM 框架 SkyWalking,异步传递上下文 transmittable-thread-local 等。

Java Agent 相对其他的方式来说,还是有一定的门槛,毕竟不是日常开发中经常会用到的技术点。如果想了解这种扩展方式,可以看看一些已经用了的开源框架的源码,就知道大概怎么使用了。下面贴一段 transmittable-thread-local 中对线程池进行扩展的代码吧,主要就是利用了 javassist 操作字节码。

try {final CtMethod afterExecute = clazz.getDeclaredMethod("afterExecute", new CtClass[]{runnableClass, throwableClass});// unwrap runnable if IsAutoWrapperString code = "$1 = com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.unwrapIfIsAutoWrapper($1);";logger.info("insert code before method " + signatureOfMethod(afterExecute) + " of class " + afterExecute.getDeclaringClass().getName() + ": " + code);afterExecute.insertBefore(code);modified = true;
} catch (NotFoundException e) {// clazz does not override afterExecute method, do nothing.
}
热门内容:
  • Java身份证号码识别系统

  • 看看人家那后端API接口写得,那叫一个优雅!

  • 给IDEA换个酷炫的主题,这个有点哇塞啊!

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡

通用的底层埋点都是怎么做的?相关推荐

  1. 测试驱动开发 测试前移_测试驱动的开发可能看起来是工作的两倍-但无论如何您都应该这样做...

    测试驱动开发 测试前移 by Navdeep Singh 通过Navdeep Singh 测试驱动的开发可能看起来是工作的两倍-但无论如何您都应该这样做 (Test-driven developmen ...

  2. 实用场景解析:那些漂亮的可视化图表都是如何做的?

    数据分析越来越重要,日常工作中,我们经常会用到数据可视化,可在实际分析做图中,我们的图总是做的太丑,别人那些漂亮的图都是如何做的?又或者图表组件太多,到底该用哪一个好呢? 今天我们将介绍6种可视化图表 ...

  3. 【转载】突然觉得今年的时间过得太快了,不知不觉中还剩下四个多月就步入2017年了,回首一下,好像什么都还没做呢。...

    2019独角兽企业重金招聘Python工程师标准>>> 突然觉得今年的时间过得太快了,不知不觉中还剩下四个多月就步入2017年了,回首一下,好像什么都还没做呢. 在职场的时候,天天盼 ...

  4. “请先做自我介绍”:高手都是这样做自我介绍,这样介绍最加分

    "请先做自我介绍":高手都是这样做自我介绍,这样介绍最加分 [转载] 我是一个什么都喜欢准备好的人,如果不准备好,感觉自己心理会没有底,就是不打无准备的战. 记得有一次参加面试,我 ...

  5. 90%的人都不会做的一道笔试题

    关注"Java后端技术全栈" 回复"面试"获取全套大厂面试资料 数组操作的题目,有的确实比较容易,但并非每个问题都是如此.今天就来看道90%的人都不会做的笔试题 ...

  6. 有些事情现在不做一辈子就都不会做了

    这句话最近一直印在我的脑海里. 这句话最早是在Casperkid的百度空间里面看见的,那时他生日.作为师傅的刺(道哥)送了他自己写的一本<白帽子讲WEB安全>给他,并在扉页上写着这句话.那 ...

  7. html超链接去虾线,挑虾线别只会用牙签了,渔民都是这样做的,1秒处理一个,超方便...

    现在大家的生活条件已经越来越好,所以说人们在城市的时候也是越来越注意饮食这一方面了,但其实大家在做饭的时候都喜欢买上一些营养丰富的食材,就像是在最近几年海鲜也是让大家都非常喜欢的.海鲜是我们日常生活中 ...

  8. 程序员工资高,但为什么越来越多的人都不再愿意做程序员呢?

    为什么IT行业这么火,各种培训机构层出不穷.很大的一点就是,只需要在写字楼坐着,薪资也高. 有些人喜欢做程序员,是因为他们不光可以通过做这份工作拿到高工资,同时也可以实现他们对技术高境界的追求:而有的 ...

  9. 许多年轻人,尤其是刚毕业走上社会的年轻人,都误以为做销售很赚钱

    许多年轻人,尤其是刚毕业走上社会的年轻人,都误以为做销售很赚钱,都想进入销售行业,挣快钱. 其实销售行业根本不是想的那么回事. 任何行业真正赚大钱的,都是极少数人,大多数人都是陪跑的,大多数人最终都只 ...

最新文章

  1. 单元测试 Mocking 类库需具备的特性
  2. C语言 | 卡尔曼滤波器算法1——应用介绍(Matlab simulink)
  3. datatables每页显示数据刷新后不变_2019北京积分落户名单数据的一些分析
  4. mysql 回表查询优化_MySQL优化:如何避免回表查询?什么是索引覆盖?
  5. 数据结构(十一)桶排序
  6. cpu压力测试 Android,Android App 压力测试方法(Monkey)
  7. CuteChat for Community Server 2.0 beta 3!
  8. 把WebForm移植到.Net MVC中
  9. 科学计算与可视化python_Python科学计算和可视化
  10. 异常和Log4j日志
  11. 数据结构和算法知识点整理
  12. LeViT: aVision Transformer in ConvNet‘s Clothing for Fast in
  13. linux pv命令,linux运维系列pv指令
  14. 一个很棒的字帖生成器
  15. 2023年度数学建模竞赛汇总
  16. OpenGL ES EGL eglSwapBuffer
  17. Android实例,实现左右滑动查看相册,图片切换器的应用
  18. ERP-非财务人员的财务培训教(四)------公司/部门的成本与费用控制收藏
  19. JVM-虚拟机栈之动态链接
  20. BZOJ2331: [SCOI2011]地板

热门文章

  1. 爬虫与浏览器的区别,爬虫产生(出自简书)
  2. 【转载】locust性能测试3
  3. php 网站内容采集器 Snoopy
  4. Python 爬取网页HTML代码
  5. AngularJs $cacheFactory 缓存服务
  6. AE,按照属性值关系选择要素
  7. PHP中spl_autoload_register函数的用法
  8. 技术图文:Python的属性装饰器详解
  9. centos 默认mysql_centos改变mysql默认目录
  10. 蓝牙写入数据库_蓝牙 数据写入 简单易懂版(适合没写过蓝牙的看)