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 {
        @Override
        public void addUpdateCount(int updateCount) {
            SpringMethodStat springMethodStat = SpringMethodStat.current();
            if (springMethodStat != null) {
                springMethodStat.addJdbcUpdateCount(updateCount);
            }
        }

        @Override
        public void addFetchRowCount(int fetchRowCount) {
            SpringMethodStat springMethodStat = SpringMethodStat.current();
            if (springMethodStat != null) {
                springMethodStat.addJdbcFetchRowCount(fetchRowCount);
            }
        }

        @Override
        public void executeBefore(String sql, boolean inTransaction) {
            SpringMethodStat springMethodStat = SpringMethodStat.current();
            if (springMethodStat != null) {
                springMethodStat.incrementJdbcExecuteCount();
            }
        }

        @Override
        public 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调试下,大概就能知道到底干嘛了。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DC电源模块有哪些注意事项和使用技巧?

BOSHIDA DC电源模块有哪些注意事项和使用技巧? DC电源模块的注意事项和使用技巧包括以下几点: 1. 选择适当的电源模块:根据需要选择合适的电源模块,考虑电压、电流和功率等参数。确保模块能够满足所需的电力要求。 2. 输入电压范…

销售如何挖掘客户?有哪些方法?

在当今竞争激烈的市场环境中,客户资源的挖掘已经成为企业生存和发展的关键。销售人员需要掌握一定的技巧和方法,以有效地发掘潜在客户,提高销售业绩。以下分享一些挖掘客户的常用方法,帮助销售人员更好地开展业务。 一、了解客户需…

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

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

Tailwind CSS 原子化开发初体验

Tailwind CSS 的工作原理是扫描所有 HTML 文件、JavaScript 组件以及任何模板中的 CSS 类(class)名,然后生成相应的样式代码并写入到一个静态 CSS 文件中。他快速、灵活、可靠,没有运行时负担。再也不用为了取一个 classname 类名…

GAMES101-LAB1

文章目录 一、问题总览二、作业参考2.1 get_projection_matrix()函数2.2 static bool insideTriangle()函数2.3 rasterize_triangle() 三、附件 一、问题总览 在屏幕上画出一个实心三角形,换言之,栅格化一个三角形实现并调用函数rasterize_triangle(con…

【MySQL基础】:超详细MySQL完整安装和配置教程

🎥 屿小夏 : 个人主页 🔥个人专栏 : MySQL从入门到进阶 🌄 莫道桑榆晚,为霞尚满天! 文章目录 📑前言一. MySQL数据库1.1 版本1.2 下载1.3 安装1.4 客户端连接 🌤️全篇总…

python使用selenium控制浏览器进行爬虫

这里以谷歌浏览器为例,需要安装一下chromedriver,其他浏览器也有相对应的driver,chromedriver下载地址:https://googlechromelabs.github.io/chrome-for-testing/ 然后是打开python环境安装一下依赖pip install selenium&#xf…

json-server实现数据Mock

json-server是一个node包,可以在不到30秒内获得零编码的完整的Mock服务实现步骤:1. 项目内安装json-server ---> npm i -D json-server2. 准备一个json文件 在根目录下(src同级)创建server文件夹,创建data.json文…

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化

字符串匹配算法是在实际工程中经常遇到的问题,也是各大公司笔试面试的常考题目,本文主要介绍BF算法(最好想到的算法,也最好实现)和KMP算法(最经典的) 一、BF算法 BF算法,即暴力(Bru…

[java] 转义字符控制台最常用快捷键

转义字符 1)\t:一个制表位,实现对齐的功能 2)\n:换行符 3) \\:一个\ 4)\":一个" 5) \:一个 6) \r:一个回车System.out.println(我亦无他AA\r唯…

Wordpress对接Lsky Pro 兰空图床插件

Wordpress对接Lsky Pro 兰空图床插件 wordpress不想存储图片到本地,访问慢,wordpress图片没有cdn想要使用图床,支持兰空自定义接口 安装教程—在wp后台选择插件zip—然后启用—设置自己图床API接口就ok了,文件全部解密&#xff0c…

pytorch 踩坑

pytorch 踩坑 在pytorch中,如果你定义了没用的组件,同样也会影响你的模型(我也不知道从哪里影响的),看一个例子 def _make_layer(self, block, planes, blocks, stride1, dilateFalse):norm_layer self._norm_layer#downsample Noneprevio…

SQL变更评审常见问题分享

SQL变更评审分享 概述 SQL变更,是我们在开发迭代中不可避免的场景,SQL变更通常是指DDL和DML语句变更,这些sql会影响到数据库表结构或具体数据,变更时如果执行到存在问题的sql脚本,会对实际应用操作难以评估的损失&…

Tg5032smn:高稳定性105℃高温

TG5032SMN是一款频率范围10MHz ~ 54MHz,具有高稳定的TCXO晶振,可与CMOS或限幅正弦输出。外部尺寸5.0 3.2 1.45mm,超小型,质地轻。该系列晶振的额定工作范围-40℃~﹢105C内可高稳定性工作,使得信号频率的误差很小。TG5032SMN与其他…

使用 Postman 进行并发请求:实用教程与最佳实践

背景介绍 最近,我们发起了一个在线图书管理系统的项目。我负责的一个关键模块包括三个主要后台接口: 实现对books数据的检索。实施对likes数据的获取。通过collections端点访问数据。 应对高流量的挑战 在设计并部署接口时,我们不可避免地…

Unity网格篇Mesh(一)

Unity网格篇Mesh(一) 本文的目标1.渲染仔细看下面的图你会发现,锯齿状 2.创建网格顶点4 x 2网格网格的顶点 3.创建网格网格只在Play模式下显示逆时针和顺时针三角形第一个三角面一个四边形由两个三角面组成第一个四边形填充剩余网格 接下一篇…

linux运行可执行文件,通过c语言调用java的main方法

前言&#xff1a;以前一直在做Android开发&#xff0c;在某本书上看过一句话“Android上面不只有App类的程序可以运行&#xff0c;能在linux下运行的程序&#xff0c;也可以在Android上面运行” 一.编写C语言部分代码 1.定义java.h头文件 #include <jni.h>#ifndef _JAV…

CSS新手入门笔记整理:动画

在CSS3中&#xff0c;我们可以使用animation属性来实现元素的动画效果。animation属性和transition属性的区别。 transition属性只能将元素的某一个属性从一个属性值过渡到另一个属性值。只能实现一次性的动画效果。animation属性来可以将元素的某一个属性从第1个属性值过渡到…

ubuntu18.04下安装vue

1、更新源 sudo apt update 2、安装nodejs sudo apt install nodejs 查看node版本 nodejs -v 但是此处要的版本是v16.14.2版本&#xff0c;所以要更新 # 查看当前node版本 node -v# 清除npm缓存 npm cache clean -f# 全局安装n npm install -g n# 升级到最新稳定版 n sta…

适用于车载电动升窗器的解决方案

升窗器是指避免车主忘记关窗的自动关窗装置&#xff0c;主要通过电子模块加认组合&#xff0c;利用主机上的芯片里面设定的程序完成检测功能&#xff0c;使自动升窗步骤顺利完成。 ■ 基于ACM32F403系列MCU ■ 高性价比软件控制方案&#xff0c;高算力 ■ MCU内置2路CAN总线&a…