Druid源码阅读-DruidStatInterceptor实现

news2025/2/24 15:11:16

上次我们在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调试下,大概就能知道到底干嘛了。

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

服务器代码上传到gitlab

服务器代码上传到gitlab 安装Git工具linux:安装Git,使用自带的源安装 yum install git生成密钥文件:使用ssh-keygen生成密钥文件.ssh/id_rsa.pub ssh-keygen 使用cat命令查看密钥,将下面的密钥复制一份 在gitlab上建立一个和…

2023年12月16日~12月22日(自适应反馈机制下基于卷积神经网络的高清晰反射波反演算法:CNN-RWI)

标题:Adaptive Feedback Convolutional-Neural-Network-Based High-Resolution Reflection-Waveform Inversion 全波形反演(FWI)是一种非线性拟合观测地震记录从而获得高清晰速度模型的最优化算法。FWI能够通过拟合浅层初至波和反射波获得较准…

浅学JWT跨域认证

Json Web令牌简称JWT 由HeaderPayloadSignature组成 Header JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。…

湘沪数字产业(上海)协同创新中心正式启动!

前言 随着全球化的加速和市场竞争的日益激烈,产业协作已成为推动科技创新和经济发展的重要手段。在创新驱动战略的推动下,全国形成了在科创核心城市建设科创中心的浪潮,旨在充分利用不同区域的产业优势,加强产业协作,…

BFS解决FloodFill算法相关leetcode算法题

文章目录 1.图像渲染2.岛屿数量3.岛屿的最大面积4.被围绕的区域 1.图像渲染 图像渲染 class Solution {int dx[4] {0,0,1,-1};int dy[4] {1,-1,0,0}; public:vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int…

计算机视觉技术-使用图像增广进行训练

让我们使用图像增广来训练模型。 这里&#xff0c;我们使用CIFAR-10数据集&#xff0c;而不是我们之前使用的Fashion-MNIST数据集。 这是因为Fashion-MNIST数据集中对象的位置和大小已被规范化&#xff0c;而CIFAR-10数据集中对象的颜色和大小差异更明显。 CIFAR-10数据集中的前…

【MIMO 从入门到精通】[P4]【MIMO Communications】

前言&#xff1a; Explains the main approaches to multi-input multi-output (MIMO) communications, including Beamforming, Zero Forcing, and MMSE. * Note that at the 9:19min mark, I made a slight "voice typo", where I should have said: "you nee…

AI进化太快了!Stability AI开源视频生成大模型Stable Video Diffusion

对于 Stable Diffusion&#xff0c;想必读者朋友们对此都不陌生。 自 Stability AI 公司发布 SD&#xff08;全称&#xff1a;Stable Diffusion) 以来&#xff0c;受到了很多人的喜爱。 SDXL 效果 随后技术升级&#xff0c;又发布了 SDXL&#xff0c;名字很有喜感&#xff0c…

计网04-网络传输介质

物理层&#xff08;网卡、传输介质&#xff09; 一、信号 &#xff11;、概念 进行网络通信在线缆中传输的就是信号&#xff0c;网线传输电信号&#xff0c;光纤传输光信号。 信息&#xff1a;对现实事物存在的某种认识数据&#xff1a;描述某些属性的具体的量子&#xff0…

C/C++学习笔记十三 C++中的重载运算符

1、什么是运算符重载&#xff1f; 运算符重载是 C 中的一项功能&#xff0c;使运算符&#xff08;例如 、- 等&#xff09;能够处理用户定义的数据类型。这种机制称为编译时多态性&#xff0c;并提供了为不同数据类型定制运算符行为的优点。 例如&#xff0c;我们可以重载“”运…

基于SpringBoot实现一个可扩展的事件总线

基于SpringBoot实现一个可扩展的事件总线 前言 在日常开发中&#xff0c;我们经常会用到事件总线&#xff0c;SpringBoot通过事件多播器的形式为我们提供了一个事件总线&#xff0c;但是在开发中我们经常会用到其他的实现&#xff0c;比如Guava、Disruptor的。我们将基于Spri…

迪杰斯特拉算法详解

迪杰斯特拉算法详解 首先要知道的是&#xff0c;迪杰斯特拉算法是求解单源最短路径的&#xff0c;就是在一个图中&#xff08;无向图和有向图均可&#xff09;&#xff0c;指定一个源点&#xff0c;求出来这个源点到其他各个节点的最短路径。 存图 首先&#xff0c;我需要用…

HarmonyOS应用兼容稳定性云测试

兼容性测试 兼容性测试主要验证HarmonyOS应用在华为真机设备上运行的兼容性问题&#xff0c;包括首次安装、再次安装、启动、卸载、崩溃、黑白屏、闪退、运行错误、无法回退、无响应、设计约束场景。具体兼容性测试项的详细说明请参考兼容性测试标准。 兼容性测试支持TV、智能穿…

爬虫系列----Python解析Json网页并保存到本地csv

Python解析JSON 1 知识小课堂1.1 爬虫1.2 JSON1.3 Python1.4 前言技术1.4.1 range1.4.2 random1.4.3 time.sleep1.4.4 with open() as f: 2 解析过程2.1 简介2.2 打开调试工具2.3 分析网址2.3.1 网址的规律2.3.2 网址的参数 2.4 爬取第一页内容2.5 存入字典并获取2.6 循环主体数…

DolphinScheduler 介绍及系统架构

目录 一、DolphinScheduler 介绍 1.1 关于 DolphinScheduler 1.2 特性 简单易用 丰富的使用场景 High Reliability High Scalability 1.3 名词解释 1.3.1 名词解释 1.3.2 模块介绍 二、DolphinScheduler 系统架构 2.1 系统架构图 2.2 架构说明 MasterServer 该服…

【Java核心基础】一文带你了解Java中super关键字的重要作用

“super”关键字在编程中扮演着重要角色&#xff0c;它允许我们直接访问父类中的属性、方法或构造函数&#xff0c;即使子类中存在同名元素。此外&#xff0c;“super()”在子类构造函数中调用父类初始化操作&#xff0c;确保父类属性正确初始化。有时&#xff0c;“super”还可…

Python 爬虫之下载视频(五)

爬取第三方网站视频 文章目录 爬取第三方网站视频前言一、基本情况二、基本思路三、代码编写四、注意事项&#xff08;ffmpeg&#xff09;总结 前言 国内主流的视频平台有点难。。。就暂且记录一些三方视频平台的爬取吧。比如下面这个&#xff1a; 一、基本情况 这次爬取的方…

OpenHarmony之内核层解析~

OpenHarmony简介 技术架构 OpenHarmony整体遵从分层设计&#xff0c;从下向上依次为&#xff1a;内核层、系统服务层、框架层和应用层。系统功能按照“系统 > 子系统 > 组件”逐级展开&#xff0c;在多设备部署场景下&#xff0c;支持根据实际需求裁剪某些非必要的组件…

【RocketMQ】Console页面报错:rocketmq remote exception,connect to xxx failed.

现象 console报错&#xff0c;无法连接该节点&#xff0c;把该节点杀掉&#xff0c;还是继续报错&#xff0c;重启之后&#xff0c;报错的端口变成11911。 分析 正常一个broker会启动三个端口&#xff0c;不同版本的规律不太一样&#xff0c;4.X版本是&#xff1a; 配置文件…