首页 > 其他分享 >SpringBoot使用@Async和@Transactional注解优化接口

SpringBoot使用@Async和@Transactional注解优化接口

时间:2022-08-31 16:56:06浏览次数:60  
标签:dailyMaintenance SpringBoot Transactional id executor Async asyncTaskExceptionLo

1、业务背景:

  项目上有一个接口需要按照前端传递的时间段范围修改6个表的数据,接口V1版本开发完成是使用的同步方式全局@Transactional注解的方式去做的,但存在一个问题就是在这六个表中,sc_xxx_rtd和sc_xxx_minute的表数据量巨大,导致接口RT时间达到了几十秒的程度,严重影响用户使用。

2、优化思路:

  使用@Async注解配合自定义异步线程池将修改6️⃣个表数据的update操作异步执行,并使用@Transactional主键保证修改数据这个操作的原子性,要不全部成功,要不全部失败;

3、代码实现:

 1 /**
 2  * 异步线程池配置
 3  *
 4  * @author zhanglei
 5  * @date 2022-02-28 9:34
 6  */
 7 @Configuration
 8 @EnableAsync
 9 public class AsyncConfig implements AsyncConfigurer {
10 
11     @Override
12     @Bean(name = "asyncTaskExecutor")
13     public Executor getAsyncExecutor() {
14         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
15         executor.setCorePoolSize(20);//核心线程数
16         executor.setMaxPoolSize(40);//最大线程数
17         executor.setQueueCapacity(1000);//队列大小
18         executor.setKeepAliveSeconds(300);//线程最大空闲时间
19         executor.setThreadNamePrefix("AsyncExecutor-"); //指定用于新创建的线程名称的前缀。
20         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//拒绝策略(一共四种,此处省略)
21         executor.initialize();
22         return executor;
23     }
24 
25     @Override
26     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
27         System.out.println("异常处理器!!你可以自定义异常处理器~");
28         return new MyAsyncUncaughtExceptionHandler();
29     }
30 }
31 
32 @Slf4j
33 @Component
34 class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
35 
36     @Resource
37     private AsyncTaskExceptionLogMapper asyncTaskExceptionLogMapper;
38 
39     @Override
40     public void handleUncaughtException(Throwable ex, Method method, Object... params) {
41 
42         log.error("occur exception class#method: " + method.getDeclaringClass().getName() + "#" + method.getName());
43         log.error("occur exception type        : " + ex.getClass().getName());
44         log.error("occur exception exception   : " + ex.getMessage());
45         log.error("occur exception param   : " + JSONUtil.toJsonStr(Lists.newArrayList(params)));
46 47 if (ObjectUtil.isNotNull(ex) && ObjectUtil.isNotNull(method)) { 48 AsyncTaskExceptionLog asyncTaskExceptionLog = new AsyncTaskExceptionLog(); 49 asyncTaskExceptionLog.setId(IdUtil.getSnowflakeNextIdStr()); 50 asyncTaskExceptionLog.setOccurClass(method.getDeclaringClass().getName()); 51 asyncTaskExceptionLog.setOccurMethod(method.getName()); 52 asyncTaskExceptionLog.setOccurType(ex.getClass().getName()); 53 asyncTaskExceptionLog.setOccurMessgae(ex.getMessage()); 54 if (ObjectUtil.isNotNull(params)) { 55 asyncTaskExceptionLog.setOccurParam(JSONUtil.toJsonStr(Lists.newArrayList(params))); 56 } 57 asyncTaskExceptionLogMapper.insert(asyncTaskExceptionLog); 58 } 59 60 } 61 }
 1 /**
 2      * 更新sc_data_xxx和sc_data_xxx_review的数据
 3      */
 4     @Async(value = "asyncTaskExecutor")
 5     @Transactional(rollbackFor = Throwable.class)
 6     public void updateScData(String userName, DailyMaintenance dailyMaintenance, AppMaintenanceExamine appMaintenanceExamine,
 7                              String reviseStartTime, String reviseEndTime, String id, Date beforeExceptionStartTime, Date beforeExceptionEndTime) {
 8         try {
 9             log.info("进入异步方法内");
10             //获取巡检模板ID、异常因子编码列表、设备编码
11             Integer templateId = appMaintenanceExamine.getExamineTemplateId();
12             String exceptionPolCodeStr = dailyMaintenance.getExceptionPolCodeList();
13             String deviceCode = dailyMaintenance.getDeviceCode();
14 
15             //故障异常标识
16             String flag = "D";
17 
18             List<ExceptionDataOperateLog> paramList = Lists.newArrayList();
19 
20             /*
21              * 如果异常因子编码列表为空则不修改
22              * 判断运维人员上传的异常因子编码,如果是ALL则按照设备编码和时间段去查询修改全部因子review数据
23              * 否则根据解析出的因子,按照设备编码和要修改的因子polCode以及时间段去修改对应review数据
24              * 只有查询出符合条件的数据才去执行修改,否则不去执行update,避免查询不出数据而导致修改了全表数据
25              * 也给review相关数据打标识
26              */
27             updateReviewData(reviseStartTime, reviseEndTime, exceptionPolCodeStr, deviceCode, userName, flag, paramList);
28 
29             //给原始数据打标识
30             updateOriginalFlag(reviseStartTime, reviseEndTime, templateId, exceptionPolCodeStr, deviceCode, userName, flag, paramList);
31 
32             if (CollectionUtil.isNotEmpty(paramList)) {
33                 //批量插入操作日志数据到日志记录表
34                 exceptionDataOperateLogMapper.batchInsert(paramList);
35             }
36 
37             log.info("异步方法执行完毕");
38         } catch (Exception e) {
39             log.error("异步修改数据出现异常:", e);
40             //获取Spring容器对象
41             ApplicationContext applicationContext = SpringUtil.getApplicationContext();
42             RevisalErrorEvent revisalErrorEvent = new RevisalErrorEvent(this);
43             revisalErrorEvent.setId(id);
44             revisalErrorEvent.setBeforeExceptionStartTime(beforeExceptionStartTime);
45             revisalErrorEvent.setBeforeExceptionEndTime(beforeExceptionEndTime);
46             applicationContext.publishEvent(revisalErrorEvent);
47             //手动回滚事务
48             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
49         }
50     }
/**
 * 订正异常event事件
 * @author zhangl
 * @date 2022-04-21 14:32
 */
@SuppressWarnings("ALL")
@Data
public class RevisalErrorEvent extends ApplicationEvent {

    /**
     * 日常运维表主键id
     */
    private String id;

    /**
     * 之前的异常开始时间
     */
    private Date beforeExceptionStartTime;

    /**
     * 之前的异常开始时间
     */
    private Date beforeExceptionEndTime;

    public RevisalErrorEvent(Object source) {
        super(source);
    }

}
 1 /**
 2  * @author zhangl
 3  * @date 2022-05-25 10:10
 4  */
 5 @Component
 6 @Slf4j
 7 public class RevisalErrorEventListener implements ApplicationListener<RevisalErrorEvent> {
 8 
 9     @Resource
10     private DailyMaintenanceMapper dailyMaintenanceMapper;
11 
12     /**
13      * 监听异步任务是否出现异常,出现则回滚日常运维表的数据
14      *
15      * @param event
16      */
17     @Override
18     public void onApplicationEvent(RevisalErrorEvent event) {
19         String id = event.getId();
20         Date beforeExceptionStartTime = event.getBeforeExceptionStartTime();
21         Date beforeExceptionEndTime = event.getBeforeExceptionEndTime();
22         DailyMaintenance dailyMaintenance = dailyMaintenanceMapper.selectById(id);
23         dailyMaintenance.setExceptionStartTime(beforeExceptionStartTime);
24         dailyMaintenance.setMaintenanceEndTime(beforeExceptionEndTime);
25         dailyMaintenance.setIfRevise(0);
26         dailyMaintenanceMapper.updateById(dailyMaintenance);
27     }
28 
29 }
 1     <update id="batchUpdateFlag">
 2         update sc_data_hour
 3         <set>
 4             <if test="flag != null and flag !=''">
 5                 flag = #{flag}
 6             </if>
 7         </set>
 8         <where>
 9             <if test="startDateTime != null and startDateTime != ''">
10                 and data_time &gt;= to_date(#{startDateTime},'yyyy-MM-dd hh24:mi:ss')
11             </if>
12             <if test="endDateTime != null and endDateTime != ''">
13                 and data_time &lt;= to_date(#{endDateTime},'yyyy-MM-dd hh24:mi:ss')
14             </if>
15             and flag != 'D'
16         </where>
17     </update>

 

注意事项:

  原版update的修改语句是通过where id in 的方式去修改的,但实际发现如果id的数据量特别多成千上万那种的话,sql执行的效率就非常的低,如果可以的话尽可能使用范围条件而不是in的方式

标签:dailyMaintenance,SpringBoot,Transactional,id,executor,Async,asyncTaskExceptionLo
From: https://www.cnblogs.com/Yoona520/p/16643655.html

相关文章

  • SpringBoot整合Shiro
    11、SpringBoot整合Shiro11.1、什么是ShiroApacheShiro是一个Java的安全(权限)框架。Shiro可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在Jav......
  • Neo4j在linux上的安装与Springboot的集成
    Neo4j在linux上的安装与Springboot的集成在linux安装:前提:安装配置好java环境1.下载neo4j官方社区版下载地址:https://neo4j.com/download-center/#releases或直接使......
  • @Transactional不生效的场景
    @Transactional概述@Transactional注解可以加在方法、类和接口上,加在类和接口上等于给类中的每个public方法都添加了@Transactional,在使用时尽量直接加在方法上。@Transac......
  • SpringBoot slighting matters(3)
    pom版本管理<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.0.RELEASE</versio......
  • springBoot中新建拦截器
    最近项目中需要将请求中的header中增加几个字段,第一反应就是可不可以用拦截器拦截请求后将字段放进请求头中,经过网上一番学习后发现该方案可行,故将此方法记录下来供大家参......
  • 使用 async-await 实现一个请求失败自动重新请求的函数
    在项目开发中,需要拉取GA数据放到自己的数据库中,用于做更定制化的数据分析和图表等.但是因为数据较多,GA一次性只能取得10w条数据,所以需要多次请求才能获取到......
  • 【SpringBoot】springProject‘org.springframework.boot:spring-boot-starter-parent
    IDEA中搭建Spring体系,maven子项目引入父项目,子项目的pom文件和maven插件中会提示红色错误:  EA默认会缓存Maven本地库中的依赖项,导致引入的依赖版本在仓库中没找到。......
  • Cannot resolve symbol ‘SpringBootApplication’-Spring-boot
    错误描述一般情况按照流程创建SpringBoot项目不会报错,但是偶尔人品爆表了可能会出现错误,例如我在创建时出现“Cannotresolvesymbol'SpringBootApplication’”错误 ......
  • springboot mysql 的赖配置
    1、报错点  ##mysqlspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://127.0.01:3307/distributed-lock-test?......
  • SpringBoot 生产中 16 条最佳实践
    1、使用自定义BOM来维护第三方依赖1<dependencyManagement>2<dependencies>3<dependency>4<groupId>io.spring.platform</groupId>......