Druid源码阅读-DruidStatInterceptor实现

上次我们在druid-spring-boot-starter里面看到有一个DruidSpringAopConfiguration的配置类,然后引入了DruidStatInterceptor这样一个切面逻辑。今天我们就来看一下这个类的实现。

DruidStatInterceptor

这个类的包路径下入com.alibaba.druid.support.spring.stat。它定义了一个切面,所有符合这个切面的切点表达式都会被拦截执行增强逻辑,这个切点定义可以在配置文件里面设定,通过spring.datasource.druid.aop-patterns配置即可。

它定义了一个advice,其核心逻辑就是下面的invoke方法:

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {SpringMethodStat lastMethodStat = SpringMethodStat.current();SpringMethodInfo methodInfo = getMethodInfo(invocation);SpringMethodStat methodStat = springStat.getMethodStat(methodInfo, true);if (methodStat != null) {methodStat.beforeInvoke();}long startNanos = System.nanoTime();Throwable error = null;try {return invocation.proceed();} catch (Throwable e) {error = e;throw e;} finally {long endNanos = System.nanoTime();long nanos = endNanos - startNanos;if (methodStat != null) {methodStat.afterInvoke(error, nanos);}SpringMethodStat.setCurrent(lastMethodStat);}
}

这个方法里面有几个类,是druid自己定义的,简单解释下。

SpringMethodInfo

spring方法的抽象,记录了spring bean的方法信息,包括签名信息,目标类以及方法的Method对象。

SpringMethodStat

spring方法的状态抽象,记录了很多spring方法监控的指标,比如方法正在执行的次数,方法最大并发执行数,方法执行次数,方法执行时间,jdbc执行次数,更新条数等。下面是部分参数截图,实际还有很多其他的参数,有兴趣的可以自己研究下。

在这里插入图片描述

注意这里面的监控其实分两块,常规的监控其实只监视数据库侧,即这里jdbc相关的信息,方法执行其实是不会监控的,而定义了spring.datasource.druid.aop-patterns还会监控方法的执行相关信息。然后因为web请求可能多个线程调用一个方法,所以用的一个ThreadLocal记录,避免每个线程记录的值互不影响。

invoke方法

我们来具体看下invoke方法,首先拿到当前线程的最后一次方法状态信息,然后拿到对应的方法信息,注意这里的方法默认最多只支持10层代理,如果超过10层代理,也只能拿到第10层的。然后你可以看到这个方法里面其实有两个SpringMethodStat,一个是lastMethodStat,这个好像没什么实际用处,也可能我没太看明白,有清楚的朋友可以评论区说明下;还有一个是methodStat,每次修改值其实是在这个里面。这个methodStat其实是从SpringStat这个静态类的一个concurrentMap里面根据SpringMethodInfo拿到对应的SpringMethodStat,如果你的SpringMethodInfo是同一个那么修改的状态也就是一样的。

如果methodStat有值,就执行前置逻辑:

    public void beforeInvoke() {currentLocal.set(this);int running = runningCount.incrementAndGet();for (; ; ) {int max = concurrentMax.get();if (running > max) {if (concurrentMax.compareAndSet(max, running)) {break;}} else {break;}}executeCount.incrementAndGet();Profiler.enter(methodInfo.getSignature(), Profiler.PROFILE_TYPE_SPRING);}

前置逻辑很简单对runningCount,concurrentMax,executeCount这三个值进行更新,将其暂时存到Profiler的ThreadLocal里面。然后执行切点方法逻辑,最后执行后置逻辑:

    public void afterInvoke(Throwable error, long nanos) {runningCount.decrementAndGet();executeTimeNano.addAndGet(nanos);histogramRecord(nanos);if (error != null) {executeErrorCount.incrementAndGet();lastError = error;lastErrorTimeMillis = System.currentTimeMillis();}Profiler.release(nanos);}

后置方法会更新runningCount,executeTimeNano,histogramRecord,lastErrorTimeMillis等值,最后将结果存到Profiler里面的statsMapLocal里面,最后显示的数据会从Profiler里面拿。

这里代码有一个写的比较优雅的地方是,在执行当前逻辑的时候用的return invocation.proceed();,后置逻辑是在finally块里面写的。充分利用了finally里面的代码一定会执行的特性,将代码写的很简洁,这块值得借鉴。

有的朋友可能会一问,关于jdbc的执行次数修改在哪实现的呢?这里完全没有看到呢?也是在这个类里面,它里面有个内部类SpringMethodContextListener就是实现的这块逻辑,拿到SpringMethodStat,然后执行对应的修改动作即可。

它在bean初始化的时候会被注册到StatFilterContext里面,然后在执行sql的时候会利用观察者模式调用所有监听器直接各自逻辑。

   class SpringMethodContextListener extends StatFilterContextListenerAdapter {@Overridepublic void addUpdateCount(int updateCount) {SpringMethodStat springMethodStat = SpringMethodStat.current();if (springMethodStat != null) {springMethodStat.addJdbcUpdateCount(updateCount);}}@Overridepublic void addFetchRowCount(int fetchRowCount) {SpringMethodStat springMethodStat = SpringMethodStat.current();if (springMethodStat != null) {springMethodStat.addJdbcFetchRowCount(fetchRowCount);}}@Overridepublic void executeBefore(String sql, boolean inTransaction) {SpringMethodStat springMethodStat = SpringMethodStat.current();if (springMethodStat != null) {springMethodStat.incrementJdbcExecuteCount();}}@Overridepublic void executeAfter(String sql, long nanos, Throwable error) {SpringMethodStat springMethodStat = SpringMethodStat.current();if (springMethodStat != null) {springMethodStat.addJdbcExecuteTimeNano(nanos);if (error != null) {springMethodStat.incrementJdbcExecuteErrorCount();}}}------------------------省略-------------------------}
总结

总得说起来这块实现还是挺清晰的,设定切点,然后在切点前后记录相关数据,其中在看源码的时候可能有的细节地方觉得不太清晰,不懂它干嘛的,可以写个单元测试或者demo,debug调试下,大概就能知道到底干嘛了。

记住看完源码能让你更好的使用框架这只是第一层,更多的是学习他们写代码的方式,学习优秀开源项目的设计思维,然后你就知道了怎么去写优秀的代码,看的多了,你代码实力自然就上去了,想写出“低质量“的代码,也难。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/298356.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中国肺癌情形

写在前面 再看下中国肺癌的情形 综述 文章名期刊影响因子Non-small cell lung cancer in ChinaCancer Commun16.2 摘要 风险因子:吸烟史、家族史、放射暴露、空气污染、慢性肺病 晚期PD-1/PD-L1抑制剂单药使用或联合化疗药物作为标准治疗。局部肺癌晚期&#xf…

搭建react+ant design pro+umi 项目框架

一、 写本文的原因 我搭建reactantdumi这个框架的原始资料主要是来源于(ReactUmi4从零快速搭建中后台系统保姆级记录教程(一、项目创建及初始化)) 而我写这篇文章的本意就是用来记录我用搭建时候的步骤汇总。 二、 npm和node版…

PyQt5和Qt designer的详细安装教程

Qt designer界面和所有组件功能的详细介绍参考:https://blog.csdn.net/qq_43811536/article/details/135186862?spm1001.2014.3001.5501 目录 0. 写在前面1. Anaconda创建虚拟环境2. 安装PyQt5和Qt designer3. 测试安装成功 0. 写在前面 Qt Designer是Qt提供的一种…

小白入门之安装MYSQL

重生之我在大四学JAVA 第三章 安装MYSQL 把MySQL复制到要安装的路径下解压 到解压后的bin路径下复制路径 接着以“管理员”身份打开命令行(如下图所示) 注意:一定要是管理员身份,否则由于后续部分命令需要权限,出现错误! 转到…

领像M100、L100系列打印机卡纸解决方法

故障现象: 联想打印软件或者机器电源键提示卡纸,具体故障报错如下: 软件内提示内部卡纸: 机器面板红灯常亮: 解决方案: 遇上述故障现象说明打印机内部卡纸,参考以下图文操作步骤解决。 1、机器断电并打开出纸口上盖&…

sklearn 逻辑回归Demo

逻辑回归案例 假设表示 基于上述情况,要使分类器的输出在[0,1]之间,可以采用假设表示的方法。 设 h θ ( x ) g ( θ T x ) h_θ (x)g(θ^T x) hθ​(x)g(θTx), 其中 g ( z ) 1 ( 1 e − z ) g(z)\frac{1}{(1e^{−z} )} g(z)(1e−z)1​…

IP编址,IP地址介绍与子网划分方法

网络层位于数据链路层与传输层之间。网络层中包含了许多协议,其中最为重要的协议就是IP协议。网络层提供了IP路由功能。理解IP路由除了要熟悉IP协议的工作机制之外,还必须理解IP编址以及如何合理地使用IP地址来设计网络。 上层协议类型 以太网帧中的Typ…

实习课知识整理5:在首页实现登录

项目情景:当我们未登录进入一个购物网站时,这是我们突然想要登录了,我们就可以使用首页上的登录按钮,直接登录 方法1:非常简单 直接将登录的接口放到action属性中,这边登录怎么实现的,可以参考&…

超维空间S2无人机使用说明书——51、使用yolov8进行目标跟踪

引言:为了提高yolo识别的质量,提高了yolo的版本,改用yolov8进行物体识别,同时系统兼容了低版本的yolo,包括基于C的yolov3和yolov4,以及yolov7。 简介,为了提高识别速度,系统采用了G…

大数据----MapReduce实现统计单词

目录 一、简介二、实现单词统计数据准备编程MapReduceJob 三、运行四、结果 一、简介 Hadoop MapReduce 是一个编程框架,它可以轻松地编写应用程序,以可靠的、容错的方式处理大量的数据(数千个节点)。 正如其名,MapReduce 的工作模式主要分…

Python如何将图片转换成字符

PIL(Python Image Library)库是Python平台上一个功能强大的图像处理标准库,支持图像的存储、显示和处理,几乎可以处理所有图片格式,如图像的压缩、裁剪、叠加、添加文字等等。 安装PIL库:pip install pillow from PIL import Image ascii_cha…

Cross-Drone Transformer Network for Robust Single Object Tracking论文阅读笔记

Cross-Drone Transformer Network for Robust Single Object Tracking论文阅读笔记 Abstract 无人机在各种应用中得到了广泛使用,例如航拍和军事安全,这得益于它们与固定摄像机相比的高机动性和广阔视野。多无人机追踪系统可以通过从不同视角收集互补的…