Cacheable注解导致线程block

优质博文:IT-BLOG-CN

环境:jdk8/tomcat7

一、问题现象

应用系统再一次发布回退时,cpu util突然徒增到100%以上,与此同时,服务响应时间大幅度增加,依赖该服务的应用接连抛出超时异常,发生熔断。

二、问题分析

根据实际监控情况,发现除了cpu负载和超时引起的错误突然提高外,剩余指标mem并无明显变化,所以排除掉OOM的情况,推测可能与回退时生产的并发请求相关。

根据上述分析,在cat上对堡垒机进行ThreadDump(一个选项栏),发现如下异常:
【1】http的线程数在blacking时出现大幅度增加,增大到线程池最大值1024blocking的线程数也是大幅度增长,由于业务代码中没有加锁的操作,所以第一反应是很不合理。
【2】http线程出现大量锁竞争,并且都是在相同的方法中获取锁时产生的block

当我们点击block中的线程时,可以看到如下详细信息:

当点击 锁竞争详情 按钮时,可以看到具体的错误代码位置。

三、问题排查

代码分析: 点击锁竞争详情可以看到在Excutable类中的declaredAnnotations()方法存在临界区,该方法是一个同步方法,存在加锁的情况。

结合调用栈可知是服务获取城市基础数据时,调用了ROC框架中的标有@Cacheable注解的getData()缓存方法,其中AOP代码中会调用方法Method对象的getAnnotation()方法,进而调用了Excutable类的declaredAnnotations()来解析注解数据。

Executable.class

private transient volatile Map<Class<? extends Annotation>, Annotation> declaredAnnotations;
private synchronized  Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {if (declaredAnnotations == null) {// 获取declaredAnnotations}return declaredAnnotations;
} 

CityCacheRepository.class

@Cacheable(cacheNames = Cache.BASIC_DATA_CACHE_NAME, key = "#key")
public CityDataWrapper getData(String key) {// 计算返回CityDataWrapper
} 

原因分析:
【1】分析业务代码可知,代码中存在三处调用CityRepository.getData()方法,但由于业务代码中存在不合理的无效调用和多层嵌套循环,使得一次http请求会产生多达200次的getData()方法调用。
【2】服务的单台机器的tps可达80,所以计算下来每秒getData()方法调用次数可达16000次,这使得接入流量的瞬间在declaredAnnotations()方法上产生线程阻塞,并迅速消费完线程池中的所有线程,还导致cpu负载变高,响应变量的现象。

四、问题总结

解决办法: 减少getData()方法的调用次数,优化后的一次http请求,平均只需要调用7getData()方法,并通过压测发现cpu利用率不再异常升高,也不再出现线程block的情况。

思考总结:
【1】在业务代码中,尤其是在高qps场景下,需要注意服务调用过程中的临界区,压测作为一种重要的测试手段,通过并发测试可以提高暴露线程安全的问题。
【2】在业务多层嵌套的场景中,任何一种方法的不当调用可能就会产生令人难以置信的调用风暴,尤其是在高qps情况下更需要注意,合理减少嵌套层数或者减少调用次数。

五、深入拓展

对于@Cacheable注解,由于declaredAnnotations()方法存在临界区,所以这里需要测量它对于普通服务一次请求调用的影响范围。

环境: 1c2gdocker机器,order jdk8/tomcat7,其线程池保持默认的1024个。应用中定义一个使用@cacheable注解的方法,并暴露出rest接口。

压测过程:
【1】使用jmeter请求rest接口,并调用@Cacheable注解的方法,并保证一次http请求会调用一次@Cacheable注解的方法。
【2】通过不断提升qps,观察服务响应结果错误率、响应时间RT以及机器cpu状态,确保在结果错误率在0.1%以下时,观察各项指标数据。

压测结果:
【1】通过观察,在0.1%的错误率下,压测的qps最高可达800qps,此时的平均RT35ms,根据c=qps*RT,可以计算出该应用的最大并发数c=30
【2】此时观察机器状态,发现cpu利用率达到74%,并且没有出现线程block状态。

压测结论: 一次http请求调用一次@Cacheable注解方法场景下,服务在接收所能承受的最大qps时,其并没有出现线程block的情况,说明在此场景下,并不需要关注@Cacheable带来的limit

一次请求调用多次Cacheable方法时对服务的影响

环境: 与上述一致

压测过程: 这里使用jmeter请求rest接口,与上述不同的是http请求传递一个数值参数,保证一次请求可以循环调用指定数量的@Cacheable方法。

压测结果:
【1】通过观察qps=100的时候,当循环次数达到700左右的时候,也会出现线程block的情况。
【2】此时cpu利用率超过100%,响应时间会大幅度增加,但由于qps没有达到最大值,所以错误率仍在0.1下。

压测结果:
【1】此时可以计算出在qps=100时,@Cacheable注解方法调用qps达到7w左右,此时会出现线程阻塞现象,所以在一次请求中如果多次调用@Cacheable方法需要注意可能存在线程阻塞影响。
【2】由于在不同qps下,其线程并发情况不一样,所以在不同的qps下其线程block的循环次数也是不一样的,其都需要根据具体情况而定。
【3】不管是一次请求调用一次@Cacheable方法还是一次请求调用多次@Cacheable方法,都无法精确计算出由于@Cacheable导致阻塞的并发线程数,所以这里只能通过qps的维度来衡量线程阻塞的阈值。

Excutable 类

【1】在jdk1.8declaredAnnotations()方法加锁其实是通过一个单例实现的,使得线程在调用该方法时,无论单例字段是否有数据,都需要先拿锁,所以在多线程调用的情况下,会出现线程阻塞现象。
【2】@Cacheable注解代表的是一大类使用注解的方法,因此对于运行时的注解,一般都需要解析注解里的数据,也就需要调用declaredAnnotations()方法,所以在调用服务时,如果存在高频使用包括但不限于@Cacheable注解的时候,防止出现线程阻塞,最好的方式就是做压测。

jdk8Executable.class

// 单例字段
private transient volatile Map<Class<? extends Annotation>, Annotation> declaredAnnotations;
// 线程进来先拿锁,这里会出现线程阻塞
private synchronized  Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {if (declaredAnnotations == null) {// 获取declaredAnnotations}return declaredAnnotations;
} 

jdk11中对该方法的实现进行了优化,使用了懒加载的线程安全的单例模式,并不再方法上加锁,所以这块不在是瓶颈,不会长时间出现线程阻塞现象。

// 单例字段
private transient volatile Map<Class<? extends Annotation>, Annotation> declaredAnnotations;
// 线程进来先观察字段是否为null,所以只有在第一次初始化字段时,才会出现部分线程的阻塞,之后不再存在阻塞现象
private Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {Map<Class<? extends Annotation>, Annotation> declAnnos;if ((declAnnos = declaredAnnotations) == null) {synchronized (this) {if ((declAnnos = declaredAnnotations) == null) {// 获取declaredAnnotations}}}return declAnnos;
} 

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

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

相关文章

【数据分享】中国证券期货统计年鉴(2001~2023)

数据介绍 一、《中国证券期货统计年鉴(2023)》(中英文)收录了2022年证券期货市场的统计数据以及与证券期货市场相关的部分宏观经济数据&#xff0c;是一部全面反映中华人民共和国证券期货市场发展情况的资料性年刊。 二、年鉴分为概况、股票、债券、基金、期货、上市和挂牌公…

IDEA 2024.3正式版发布,速览新功能!

0 前言 IntelliJ IDEA 2024.3 引入了一系列可以提升您的开发体验的强大新功能。 IDE 现在提供代码逻辑结构的表示&#xff0c;简化了 Kubernetes 应用程序的调试体验&#xff0c;引入了集群范围的 Kubernetes 日志访问。 1 关键亮点 1.1 Structure工具窗口中的 Logical代码结…

Docker 基础命令介绍和常见报错解决

介绍一些 docker 可能用到的基础命令&#xff0c;并解决三个常见报错&#xff1a; 权限被拒绝&#xff08;Permission Denied&#xff09;无法连接到 Docker 仓库&#xff08;Timeout Exceeded&#xff09;磁盘空间不足&#xff08;No Space Left on Device&#xff09; 命令以…

【大语言模型】ACL2024论文-10 CSCD-IME: 纠正拼音输入法产生的拼写错误

【大语言模型】ACL2024论文-10 CSCD-IME: 纠正拼音输入法产生的拼写错误 目录 文章目录 【大语言模型】ACL2024论文-10 CSCD-IME: 纠正拼音输入法产生的拼写错误目录摘要研究背景问题与挑战如何解决创新点算法模型1. 错误检测模型2. 伪数据生成模块3. n-gram语言模型过滤4. 多任…

前端(2)——快速入门CSS

参考&#xff1a; 罗大富 CSS 参考手册 | 菜鸟教程 CSS 参考手册 1. CSS CSS全名是层叠样式表&#xff0c;中文名层叠样式表。用于定义网页样式和布局的样式表语言。 通过 CSS&#xff0c;你可以指定页面中各个元素的颜色、字体、大小、间距、边框、背景等样式&#xff0c;…

电阻测试流程

1.外观检查 &#xff08;1&#xff09;样品上丝印与规格书中相符&#xff0c;0402以上封装电阻要有标称电阻值&#xff0c;丝印清晰。 &#xff08;2&#xff09;检验外观&#xff0c;主要包含以下几点&#xff1a; a) 电阻器本体饱满&#xff0c;有光泽&#xff0c;不允许有气…

万博智云产品完成与ZStack Cloud云平台兼容性互认证

摘要 近日&#xff0c;上海云轴科技股份有限公司(简称“云轴科技ZStack”)与万博智云信息科技&#xff08;上海&#xff09;有限公司&#xff08;简称“万博智云OnePro Cloud”&#xff09;完成产品兼容性互认证。经过测试&#xff0c;万博智云OnePro Cloud两款旗舰产品HyperB…

深度学习框架Pytorch介绍和示例

目录 一. 简介 1.1动态计算图 1.2自动化功能 二. 主要特性 2.1 动态计算图 2.2 自动求导 2.3 强大的社区支持 2.4 多平台支持 三. 核心组件 3.1 Tensor 3.2 Autograd 3.3 nn.Module 3.4 Optim 四. 数据处理 五. 神经网络定义与训练 5.1定义神经网络&#xff1a; 5.2训练过…

鼠标点击(二)与接口函数集合的的实现

&#xff08;1&#xff09; &#xff08;2&#xff09; &#xff08;3&#xff09;

基于Spring Boot+Vue的多媒体素材管理系统的设计与实现

一.系统开发工具与环境搭建 1.系统设计开发工具 后端使用Java编程语言的Spring boot框架 项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk17 前端&#xff1a; 技术&#xff1a;框架Vue.js&#xff1b;UI库&#xff1a;ElementUI&#xff1b; 开发工具&…

《FreeRTOS列表和列表项篇》

FreeRTOS列表和列表项 1. 什么是列表和列表项&#xff1f;1.1 列表list1.2 列表项list item 2. 列表和列表项的初始化2.1 列表的初始化2.2 列表项的初始化 3. 列表项的插入4. 列表项末尾插入5. 列表项的删除6. 列表的遍历 列表和列表项是FreeRTOS的一个数据结构&#xff0c;是F…

使用 MTT GPU 搭建个人 RAG 推理服务

什么是 LLM RAG?​ LLM RAG&#xff08;Retrieval-Augmented Generation with Large Language Models&#xff09;是一种结合大语言模型&#xff08;LLM&#xff09;和信息检索&#xff08;IR&#xff09;技术的生成方法&#xff0c;专门用于增强语言模型的上下文感知和准确性…

Vue3 -- 环境变量的配置【项目集成3】

环境&#xff1a; 在项目开发过程中&#xff0c;至少会经历开发环境、测试环境和生产环境(即正式环境)三个阶段。 开发环境 .env.development测试环境 .env.test生产环境 .env.production 不同阶段请求的状态(如接口地址等)不一样&#xff0c;开发项目的时候要经常配置代理跨…

AI 大模型应用:AI开发的捷径工作流模式

一、引言 大部分人使用 AI&#xff0c;大概都跟我一样&#xff0c;停留在初级阶段。 平时&#xff0c;就是向 AI 提问&#xff08;又称聊天&#xff09;&#xff0c;偶尔也用一些现成的服务&#xff1a;生成图片、生成代码、翻译文章等等。 但是&#xff0c;时间久了&#x…

研究生被安排许多文献阅读,如何快速的阅读众多英文文献?

在科研的道路上&#xff0c;筛选文献就像是大海捞针&#xff0c;找对了方法&#xff0c;就能快速锁定那些有价值的信息。尤其是在实验方向尚未确定时&#xff0c;如何从海量文献中筛选出“金子”&#xff0c;就显得尤为重要。 关键的第一步&#xff1a;精准筛选 当你面对一堆…

信创迎来冲刺三年,国产项目管理软件跑出数智化“加速度”

信创发展是国家当前重要的战略布局&#xff0c;对国家发展具有长远的战略意义。国资委信创79号文件规定&#xff0c;2027年前按顺序完成“28N”的党政与八大重点行业100%信创替代&#xff0c;通过信创产业发展&#xff0c;国家能够提高自主创新能力&#xff0c;加速推进国产化转…

LSTM(长短期记忆网络)详解

1️⃣ LSTM介绍 标准的RNN存在梯度消失和梯度爆炸问题&#xff0c;无法捕捉长期依赖关系。那么如何理解这个长期依赖关系呢&#xff1f; 例如&#xff0c;有一个语言模型基于先前的词来预测下一个词&#xff0c;我们有一句话 “the clouds are in the sky”&#xff0c;基于&…

基于Java仓库管理系统

一、作品包含 源码数据库全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、LayUI 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA 数据库&#xff1a;MySQL8.0 数据库管…

数量关系2_余数平方等差、整除和完工

目录 一、余数、平方数与等差数列1.等差数列2.平方数3.余数问题二、整除问题和合作完工问题1.利用倍数特性解决不定方程2.利用整除特性解决纯整除问题3.合作完工一、余数、平方数与等差数列 1.等差数列 ※等比数列不常考,或者考的时候比较复杂,可放弃。 补充1:常用的等差数…

cache中命中率和缺失率

这张图解释了缓存的三个关键指标&#xff1a;命中率、缺失率和缺失损失&#xff0c;并分析了它们在缓存访问中的重要性。 具体说明 命中&#xff08;Hit&#xff09;&#xff1a; 命中表示要访问的信息在缓存中已经存在&#xff0c;不需要从更慢的主存中读取。命中率&#xff…