Spring中使用Async进行异步功能开发实战-以大文件上传为例

06-01 1418阅读

目录

前言

一、场景再现

1、Event的同步机制

二、性能优化

1、异步支持配置

2、自定义处理线程池扩展

3、将线程池配置类绑定到异步方法

三、总结


前言

        在之前的博客中,曾将讲了在SpringBoot中如何使用Event来进行大文件上传的解耦,原文地址:使用SpringEvent解决WebUploader大文件上传解耦问题,在这篇博客当中,我们使用Event机制成功的将大文件的上传和解析的功能进行分离,已经实现了解耦的需求。但是在真实项目中会存在一个问题,就是解耦是解耦了。但是我们期望程序能够做到异步,也就是将文件的上传和解析进行彻底的异步化。后台程序在接收前端请求的文件时,文件上传完成后就结束。而对于上传文件的处理和解析等操作则放到解析程序中。整个过程给人的感觉就是到上传就完成了,解析则可以在后台慢慢运行,等待执行完成即可。

Spring中使用Async进行异步功能开发实战-以大文件上传为例

        这里我们仍然以大文件上传为例,首先讲解在未进行程序异步化的时候,程序的运行机制和表现。然后讲解如何进行异步化的改造,让程序进行异步执行。通过本文不仅能让你掌握如何进行Event的事件开发,同时还能掌握在Spring中如何进行异步开发,熟悉@Async的具体用法。

一、场景再现

        为了能让大家对故事的场景有更加直观的认识,这里我们将场景进行再现,让大家看到具体的问题。带着问题,我们一起来寻找解决办法,这样对前因后果更加清楚。

1、Event的同步机制

        首先我们来看一下原来的事件分离处理代码,关键代码如下:

@EventListener
public void fileUploadEventRegister(FileUploadEvent event){
	try {
		sys_user_logger.info("当前处理线程名称:" + Thread.currentThread().getName());
		FileEntity fileEntity = event.getFileEntity();
		if(StringUtils.isNotEmpty(fileEntity.getTablename())){
			FileUploadServiceRegisterEnum rigisterEnum = null;
			if(StringUtils.isNotBlank(fileEntity.getBizType())) {//业务类型不为空,则根据表名和业务名称来查找执行service
				rigisterEnum = FileUploadServiceRegisterEnum.getEnumByTableNameAndBizType(fileEntity.getTablename(), fileEntity.getBizType());
			}else {
				rigisterEnum = FileUploadServiceRegisterEnum.getEnumByTableName(fileEntity.getTablename());
			}
			if(null != rigisterEnum && StringUtils.isNotEmpty(rigisterEnum.getExecService())){
				String execService = rigisterEnum.getExecService();
				IFileUploadCallbackService service = SpringUtils.getBean(execService);
				service.process(fileEntity);
			}else{
				sys_user_logger.info("未注册文件上传监听回调处理器.");
			}
		}
	} catch (Exception e) {
		sys_user_logger.error("文件上传事件监听发生错误.",e);
	}
}

        在这里为了让文件处理的时间加长,我们可以把文件处理的逻辑加上一个线程的等待时间比如休眠等待35秒钟。代码如下:

@Override
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class)
public void process(FileEntity fileEntity) throws Exception {
	if(null != fileEntity && StringUtils.isNotEmpty(fileEntity.getBid())){
		String pkId = fileEntity.getBid();
		Student stu = studentService.selectStudentById(Long.valueOf(pkId));
		//System.out.println(fileEntity.getPath());
		//System.out.println(stu.getName() + "\t" + stu.getAddress());
		logger.info("开始处理........");
		Thread.sleep(35 * 1000);//休眠35秒测试
		logger.info("执行结束");
	}
}

        在这里,我们使用Thread.sheep这个方法来让线程进行休眠等待。模拟文件上传后,解析很慢的场景。由于卡顿变慢,下面的界面也一直处于等待的状态。

Spring中使用Async进行异步功能开发实战-以大文件上传为例

        下面是原来的处理线程信息,通过日志可以看到,相关的执行线程都是[http-nio-8080-exec-26]:

20:27:11.280 [http-nio-8080-exec-26] INFO  sys-user - [fileUploadEventRegister,32] - 当前处理线程名称:http-nio-8080-exec-26
20:27:11.281 [http-nio-8080-exec-26] DEBUG c.y.p.e.s.m.S.selectById - [debug,137] - ==>  Preparing: SELECT id,name,sex,birthday,address,remark,create_by,create_time,update_by,update_time FROM biz_student WHERE id=?
20:27:11.284 [http-nio-8080-exec-26] DEBUG c.y.p.e.s.m.S.selectById - [debug,137] - ==> Parameters: 1811048705298571266(Long)
20:27:11.287 [http-nio-8080-exec-26] DEBUG c.y.p.e.s.m.S.selectById - [debug,137] -  Parameters: 1811048705298571266(Long)
20:52:48.971 [Async-FileUpload-Thread-1] DEBUG c.y.p.e.s.m.S.selectById - [debug,137] -   Preparing: SELECT count(0) FROM biz_file WHERE (f_state = ? AND b_id = ? AND table_name = ? AND biz_type = ?)
20:52:48.984 [http-nio-8080-exec-71] DEBUG c.y.p.w.m.F.selectList_COUNT - [debug,137] - ==> Parameters: 1(Integer), 1811048705298571266(String), biz_student(String), 123a(String)
20:52:49.003 [http-nio-8080-exec-71] DEBUG c.y.p.w.m.F.selectList_COUNT - [debug,137] -   Preparing: SELECT id, f_id, b_id, f_type AS type, f_name AS name, f_desc AS desc, f_state AS state, f_size AS size, f_path AS path, table_name, md5code, directory, biz_type, create_by, create_time, update_by, update_time FROM biz_file WHERE (f_state = ? AND b_id = ? AND table_name = ? AND biz_type = ?) order by create_time desc LIMIT ?
20:53:23.973 [Async-FileUpload-Thread-1] INFO  sys-user - [process,34] - 执行结束

        可以很明显的看到,文件的解析程序是使用Async-FileUpload-Thread开头的线程来进行处理的,即表名是正常的使用线程池来进行处理相关的业务。也说明的我们的设计达到了预期,即实现了程序的完全异步化。

三、总结

        以上就是本文的主要内容,本文以大文件上传为例,首先讲解在未进行程序异步化的时候,程序的运行机制和具体表现。然后讲解如何进行异步化的改造,让程序进行异步执行。通过本文不仅能让你掌握如何进行Event的事件开发,同时还能掌握在Spring中如何进行异步开发,熟悉@Async的具体用法。行文仓促,难免有不足之处,如果有表达不当的地方或者不足之处,还请各位专家批评指正,在评论区留下您的真知灼见,万分荣幸。

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码