Springboot @Async 失效的坑

news2024/11/26 11:49:17

异步应用场景

为了提高接口的响应性能,当业务非常复杂的情况下,可以将一部分跟业务关联性不是特别强的逻辑进行异步处理。如日志记录、短信发送、增加积分等。通常而言会将此类业务逻辑通过异步的方式进行处理,从而加快接口的响应速度,常用的解决方案有:

  • 使用JDK 自定义线程池 让代码异步执行
  • 在springboot 中 使用@Async注解进行异步处理
  • 使用中间件如mq 消息通知让下游异步消费 如RocketMQ、KAFKA

使用第一种方式,需要精通线程池运行原理,结合实际的业务场景对队列大小进行合理的设置。队列设置过大过小都会存在内存溢出的风险。

第三种方式是最合理的方式,它能够通过MQ进行削峰填谷,通过合理的参数配置,保证数据不会丢失。但是架构改动过大,对小型的单体应用来讲,工作量过大,成本过高。

在springboot 大行其道的情况下,考虑开发成本,以及项目时间关系选用第二种方式来解决代码异步执行的问题。

真实业务场景

线上问题

一个工单的分页列表,前端控制了每个列表最大的显示条数为100条。在业务流程中存在工单转移的操作,转移一笔工单至少包含以下几个重要的步骤:

  1. 新增工单处理日志,如什么时间点将工单转移给某人
  2. 修改工单当前处理人
  3. 发送企业微信给B端的跟进人(转移人)
  4. 发送im信息给C端的用户

由于公司采用微服务架构,因此每个业务模块拆分的很细,在上述步骤中需要从其他系统中通过rpc调用接口拿到需要的数据才能完成整个业务流程数据的拼装,如需要从crm系统拿到组织架构信息,获取转移人的组织架构、需要从udb用户中心获取转移人的企业微信昵称等。

因此在批量转移的时候,前端会出现调用超时的问题,原因是dubbo接口默认的超时时间是15秒,由于业务复杂,导致在15秒内执行不完业务逻辑。

解决方案

  1. 将sql处理改为批量执行,如新增处理日志 (batch insert);修改当前工单,使用case when 的方式一次性修改完成(批量update)
  2. 将发送消息改成异步处理 加快前端接口的响应速度
  3. 让接口提供方提供批量查询的接口,避免rpc 循环调用在网络上的消耗

优化完成之后,接口的响应速度由15秒多,变成了1秒。但是过程中遇到坑了。特此记录一下

技术实现

优化过程

在这里插入图片描述

@Async 注解定义为可以放置在方法上和类上,当使用在类上表明类所有的方法都能异步执行。在Springboot中是需要在方法上加上该注解就可以完美的实现异步执行。

原始方法伪代码如下

/**
 * 原始代码 采用流水式的代码一步步实现 业务逻辑
 */
public void doBusiness(Object args){
  //1.  新增工单处理日志,如什么时间点将工单转移给某人
 
  //2. 修改工单当前处理人
  
  //3. 发送企业微信给B端的跟进人(转移人)
  
  //4. 发送im信息给C端的用户
}

那么异步问题就很好处理了,只需要将方法抽离形成多个子方法, 每个方法执行自己的业务处理逻辑,然后再方法加上@Async注解不就ok了么,伪代码如下

public void doBusiness(Object args){
 	//2. 修改工单当前处理人
  
  this.doAsyncBusiness();
}


// 单独抽离一个异步执行的方法 加上@Async注解
@Async
private void doAsyncBusiness(Object args){
  //1.  新增工单处理日志,如什么时间点将工单转移给某人
  
  //3. 发送企业微信给B端的跟进人(转移人)
  
  //4. 发送im信息给C端的用户
}

打完收工,重启应用,进行测试,然而并没有像预期中的那样,接口的响应速度还是15秒左右。接着排查原因,可以肯定的是@Async是可以提供异步方法执行。应该是我们使用方式不对导致。

@Async 限制

熟悉Springboot AOP的同学可能会发现更改后的代码存在明显的问题

  • 首先AOP代理机制要求 被代理的方法必须是 public , private 方法不能被代理
  • 其次AOP代理机制会生成一个代理类 执行代理方法 注意 this.doAsyncBusiness() 调用的是本对象的方法 ;
  • 在启动类上加上@EnableAsync注解

综上所述,原因我们已经通过AOP代理的原理找到了。下面摘自官方文档的一段话:

  • it must be applied to public methods only
  • self-invocation – calling the async method from within the same class – won’t work

The reasons are simple – 「the method needs to be public」 so that it can be proxied. And 「self-invocation doesn’t work」 because it bypasses the proxy and calls the underlying method directly.

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

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

相关文章

【HMS Core】集成多种HMS Core服务,让APP成为旅行小助手

1 介绍 Duration: 5:00 总览 旅行者能够使用该应用在华为地图上搜索附近名胜景点、游玩去处或是附近ATM、餐馆和医院等。 服务场景描述 Tourism应用通过集成机器学习服务的地标识别能力,能够提供旅行过程所有必要的信息,还可以识别图片中的地标建筑。…

[Python]字符串常用操作与方法

前言 系列文章目录 [Python]目录 文章目录前言1. 字符串的常用操作1.1 格式化输出1.1.1 %1.1.1.1 语法1.1.1.2 字符串格式操作符(%)的格式化符号%c:以字符的形式格式化输出整数或长度为1的字符串%r:将数据格式化为供解释器读取的形式%s:将数据…

一文带你精通Git

一文带你精通git回顾git对象树对象提交对象重新认识git 基本命令git 高层命令分支(特别重要)分支冲突&分支合并git 存储git 后悔药远程分支和团队协作远程仓库冲突回顾 博主之前直接已经写过了git的相关基础博客了,老铁可以自行去查看。本篇文章的目…

【分割链表】

目录:前言一、题目描述二、算法思想(一)值交换1、题目解析2、代码实现(二)重构链表1、题目解析2、代码实现总结前言 大家好,今天我们来了解一下leetcode中比较简单的单链表问题。 一、题目描述 题目描述如…

MySQL事务隔离级别

MySQL的四种隔离级别 读未提交读提交可重复读串行化 隔离级别可以通过MySQL的视图来实现。 读未提交 读未提交是一个事务仅修改了数据但还未提交时,本次修改可以便可被其他事务查询到变更后的值。读未提交隔离级别下,其他事务进行查询时,直…

新手小白学JAVA 日期类Date SimpleDateFormat Calendar

Date日期类 类 Date 表示一个特定的瞬间,精确到毫秒 1.1 Date的构造函数 Date() 分配一个 Date 对象,以表示分配它的时间(精确到毫秒) Date(long date) 分配一个 Date 对象,表示自从标准基准时间起指定时间的毫秒数 标…

【Java实验五】继承与多态

一、实验一 对于父类的含参构造方法,子类必须通过super调用,重写父类的构造方法 设计一个应用程序要求: 设计一个表示二维平面上点的类Point,包含有表示坐标位置的protected类型的成员变量x和y,获取和设置x和y值的publ…

SpringBoot项目的创建(三):手动创建一个Maven工程,然后引入SpringBoot所需的dependency来完成 (不需联网,但复杂)

SpringBoot项目的创建1.配置Maven环境。2.创建一个新的maven项目3.创建出来的目录结构4 继承springboot父项目5.添加Spring Boot Maven插件6.添加spring和web模块的依赖7.创建入口类8.添加代码测试1.配置Maven环境。 在以maven方式创建Spring Boot项目之前,需要先确…

交换网络基础

交换网络基础网络基础交换机的转发行为数据帧分类交换机MAC地址表ping包来回过程VLAN概念:VLAN接口类型ICMP返回值网络基础 路由器:基于3层路由表转发交换机:基于2层MAC地址表转发,数据帧转发 交换机的转发行为 泛洪&#xff1…

Buffer Pool Size of Total RAM No data

1.问题描述 1)问题现象 通过prometheus监控mysql实例和服务器,使用grafana做可视化展示,grafana 中添加 7362 号dashboard 作为mysql看板,添加 11074 号dashboard 作为主机看板。但是添加后查看 MySQL Overview 看板发现 Buffer Pool Size …

Python制作GUI学生管理系统毕设,大学生总会用得到

有很多可爱的大学生跟我吐槽: 咋这个大学跟我想象的不一样呢? 老师叫我们自己做… 还是那句话,技术才是硬道理 源码、资料电子书文末名片获取 有个经典案例就是 学生管理系统 写完了放在那也是放着,所以今天分享给大家吧&…

2022第四届长安杯复盘

容器加挂密码:2022.4th.changancup! 案件背景: 某地警方接到受害人报案称其在某虚拟币交易网站遭遇诈骗,该网站号称使用”USTD币“购买所谓的"HT币”,受害人充值后不但 “HT币”无法提现、交易,而且手机还被恶意…

PID算法

目录 一、PID算法 二、模拟PID 模拟PID调节器的控制规律为 模拟PID调节器的传递函数为 三、数字PID P控制 PI控制 PD控制 PID控制 数字PID增量型控制算式 PID位置型控制算式 一、PID算法 PID控制是最早发展的自动控制策略之一,是微机化控…

C++ opencv图像存储和MAT容器

1.图像在内存之中的存储方式: 图像矩阵的大小取决于所用的颜色模型,确切说,取决于所用通道数。如果是灰度图像,矩阵就会如图5.1所示。 对于多通道图像来说,矩阵中的列会包含多个子列,其子列个数与通道数相同&#xf…

【运筹优化】结合天际线启发式的蚁群算法求解二维矩形装箱问题 + Java代码实现

文章目录一、天际线启发式二、蚁群算法结合天际线启发式2.1 构建序列2.1.1 思路一2.1.2 思路二2.1.3 思路N三、Java代码实现3.1 项目结构3.2 Ant3.3 ACO3.4 Run3.5 运行结果展示3.5.1 思路一3.5.2 思路二3.5.3 思路N四、小结一、天际线启发式 关于天际线启发式的介绍请看我的另…

推荐算法---矩阵分解

矩阵分解报告 1. 试验介绍 矩阵分解就是把原来的大矩阵,近似的分解成小矩阵的乘积,在实际推荐计算时不再使用大矩阵,而是使用分解得到的两个小矩阵。具体来说就是,假设用户物品的评分矩阵R是m乘n维,即一共有m个用户&…

Redis资料整理

Redis--->是非关系型数据库(也称缓存数据库),是一种NoSQL数据库 存放5种数据类型 String key-value形式 另外list,set,zset,hash 另外两种不常用的:bitmap(位图类型),geo(地理位置类型),另外Redis5.0新增 stream类型 相对来说Redis适合存放少数据量的数据,如果需要存放…

BERT知识蒸馏TinyBERT

1. 概述 诸如BERT等预训练模型的提出显著的提升了自然语言处理任务的效果,但是随着模型的越来越复杂,同样带来了很多的问题,如参数过多,模型过大,推理事件过长,计算资源需求大等。近年来,通过模…

PX4基本配置

目录 下载固件 下载原生稳定版固件 安装PX4 Master, Beta或自定义固件 FMUv2 Bootloader 更新 机架设置 飞行控制器/传感器方向 计算朝向 设置朝向 罗盘校准 执行校准 陀螺仪校准 # 执行校准 加速度计 执行校准 空速计校准 执行校准 水平平面校准 执行校准 …

Spring Cloud Zookeeper 升级为Spring Cloud Kubernetes

这里是weihubeats,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党 背景 现有的微服务是使用的Spring Cloud Zookeeper这一套,实际应用在Kubernetes中部署并不需要额外的注册中心,本身Kubernetes自己就支持…