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.mzph.cn/news/241605.shtml

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

相关文章

常见激活函数

激活函数是神经网络中的一种非线性变换,它在神经元的输出上引入了非线性性质,使神经网络能够更好地学习和适应复杂的数据模式。以下是一些常见的激活函数: Sigmoid 函数 Sigmoid 函数将输入映射到(0,1)之间…

第4章 | 安徽某高校《统计建模与R软件》期末复习

第4章 参数估计 参数估计是统计建模的关键步骤之一,它涉及根据样本数据推断总体参数的过程。在统计学中,参数通常用于描述总体的特征,如均值、方差等。通过参数估计,我们可以利用样本信息对这些未知参数进行推断,从而…

JoySSL诚招SSL证书代理

不久前,阿里云宣布了一个让人稍感唏嘘的消息——它们的一年期免费SSL证书服务将停步,转而提供三个月期限的证书。这一变化,无疑会使得网站开发的公司在维持用户信任和网站安全上多出心思。然而,免费的午餐并没有彻底消失&#xff…

有关List的线程安全、高效读取:不变模式下的CopyOnWriteArrayList类、数据共享通道:BlockingQueue

有关List的线程安全 队列、链表之类的数据结构也是极常用的,几乎所有的应用程序都会与之相关。在java中, ArrayList和Vector都使用数组作为其内部实现。两者最大的不同在与Vector是线程安全的。 而ArrayList不是。此外LinkedList使用链表的数据结构实现…

Java多线程技术四——定时器

1 定时器的使用 在JDK库中Timer类主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务,Timer类的方法列表如下: Timer类的主要作用就是设置计划任务,封装任务的类却是TimerTask,该类的结构如下图 因为TimerT…

Flink 数据序列化

为 Flink 量身定制的序列化框架 大家都知道现在大数据生态非常火,大多数技术组件都是运行在JVM上的,Flink也是运行在JVM上,基于JVM的数据分析引擎都需要将大量的数据存储在内存中,这就不得不面临JVM的一些问题,比如Ja…

【小白攻略】php 小数转为百分比,保留两位小数的函数

php 小数转为百分比 首先,最简单直观的方法是利用PHP内置的number_format函数。该函数可以对一个数字进行格式化,并可以设置小数点后的精度。通过将小数乘以100,再用number_format函数将结果格式化为百分比形式,即可达到将小数转为…

【线性代数】决定张成空间的最少向量线性无关吗?

答1: 是的,张成空间的最少向量是线性无关的。 在数学中,张成空间(span space)是一个向量空间,它由一组向量通过线性组合(即每个向量乘以一个标量)生成。如果这组向量是线性无关的&…

HackTheBox - Medium - Linux - Format

Format Format 是一种中等难度的 Linux 机器,它突出显示了由解决方案的结构方式引起的安全问题。立足点涉及PHP源代码审查,发现和利用本地文件读/写漏洞,并利用Nginx中的错误配置在Redis Unix套接字上执行命令。横向移动包括浏览 Redis 数据…

旅游品牌网站搭建的作用是什么

我国旅游业规模非常高,各地大小旅游景区也是非常多,尤其节假日更是可以达到峰值,无论周边游还是外地游对所要去的景区,消费者总是需要来回了解很多,浏览器查或旅行社咨询等。 对旅游企业而言,传统线下方式…

Linux6.1、IO基础(C库函数文件类接口)

个人主页:Lei宝啊 愿所有美好如期而遇 1、文件打开及关闭 我们先了解文件的打开,path是文件路径,mode是打开文件的模式 r模式是只读,流位于文件的开头,也就是说从文件内容的开头开始读。r模式是读和写,同…

研究论文 2022-Oncoimmunology:AI+癌RNA-seq数据 识别细胞景观

Wang, Xin, et al. "Deep learning using bulk RNA-seq data expands cell landscape identification in tumor microenvironment." Oncoimmunology 11.1 (2022): 2043662. https://www.tandfonline.com/doi/full/10.1080/2162402X.2022.2043662 被引次数&#xff1…

VSCode软件与SCL编程

原创 NingChao NCLib 博途工控人平时在哪里技术交流博途工控人社群 VSCode简称VSC,是Visual studio code的缩写,是由微软开发的跨平台的轻量级编辑器,支持几乎所有主流的开发语言的语法高亮、代码智能补全、插件扩展、代码对比等&#xff0c…

图解LRU缓存

图解LRU缓存 OJ链接 介绍 LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。 双向链表按照被使用的顺序存储了这些键值对,靠近尾部的键值对是最近使用的,而靠近头部的键值对是最久未…

适配器模式学习

适配器模式(Adapter)将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 适配器模式分为类适配器模式和对象适配器模式两种,前者类之间的耦合度比后者高,且要…

【常见的语法糖(详解)】

🟩 说几个常见的语法糖 🟢关于语法糖的典型解析🟢如何解语法糖?🟢糖块一、switch 支持 String 与枚举📙糖块二、泛型📝糖块三、自动装箱与拆箱🍁糖块四、方法变长参数🖥️…

STM32——CAN协议

文章目录 一.CAN协议的基本特点1.1 特点1.2 电平标准1.3 基本的五个帧1.4 数据帧 二.数据帧解析2.1 帧起始和仲裁段2.2 控制段2.3 数据段和CRC段2.4 ACK段和帧结束 三.总线仲裁四.位时序五.STM32CAN控制器原理与配置5.1 STM32CAN控制器介绍5.2 CAN的模式5.3 CAN框图 六 手册寄存…

LangChain 30 ChatGPT LLM将字符串作为输入并返回字符串Chat Model将消息列表作为输入并返回消息

LangChain系列文章 LangChain 实现给动物取名字,LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储,读取YouTube的视频文本搜索I…

深度学习(八):bert理解之transformer

1.主要结构 transformer 是一种深度学习模型,主要用于处理序列数据,如自然语言处理任务。它在 2017 年由 Vaswani 等人在论文 “Attention is All You Need” 中提出。 Transformer 的主要特点是它完全放弃了传统的循环神经网络(RNN&#x…

智能优化算法应用:基于爬行动物算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于爬行动物算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于爬行动物算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.爬行动物算法4.实验参数设定5.算法结果6.…