SpringBoot异步处理请求
在实际项目中需要开发一个处理资源编译请求的接口:接受前台页面上传的ymal文件,后台根据模板生成scala文件,然后打包、构建docker镜像。
文件上传和后台处理过程的实现都不难,只是整个过程是比较耗时的,这里如果使用同步方式,那么上传-->模板解析-->打包-->构建镜像-->返回结果;这个过程,前台的页面都是等待状态的,用户会以为页面卡死了。所以,这里需要做异步处理:
1.上传-->返回正在编译的标志;
2.模板解析-->打包-->构建镜像-->存储编译结果;
此时,当用户上传完文件后,页面立马跳转,模板解析和镜像构建等工作,继续在后台进行,而用户可以不用等待。
由于整个接口服务采用springboot实现,这里简单记录一种springBoot的异步使用方式,
这种方式,是springBoot自身的一种异步方式,使用注解实现,非常方便,我们在想要异步执行的方法上加上@Async注解,在controller上加上@EnableAsync即可。注意这里的异步方法,只能在自身之外调用,在本类调用是无效的。
controller
@RequestMapping(value = "/XXX/xxx") @RestController @EnableAsync public class CompileController { private static final Logger log = LoggerFactory.getLogger(CompileController.class); private final static int MZX_SIZE = 51200000; @Autowired private CompileRecordRepository recordRepository; @Autowired private BackendService backend; @Value("${build.resource-dir}") private String resourceDir; /** * 编译服务 * * @param name * @param version * @param namespace * @param file * @return */ @RequestMapping(value = "/compile", method = RequestMethod.POST) public String compile(String name, String version, String namespace, @RequestParam("yaml") MultipartFile file) { if (file.isEmpty()) { return Response.error("编译失败,因为文件是空的."); } if (file.getSize() > MZX_SIZE) { return Response.error("编译失败,文件大小超過限制"); } String fileName = file.getOriginalFilename().toLowerCase(); log.info("fileName:" + fileName); if (!fileName.endsWith(".yml")) { return Response.error("must upload yml format file"); } String token = TokenUtil.generateToken(); CompileLog compileLog = new CompileLog(); compileLog.setId(token); compileLog.setName(name); compileLog.setNamespace(namespace); compileLog.setToken(token); compileLog.setVersion(version); compileLog.setCreateTime(System.currentTimeMillis()); compileLog.setUpdateTime(System.currentTimeMillis()); compileLog.setStatus(0); recordRepository.save(compileLog); //读取文件内容并写到本地 String ymlStr = readAndWrite2Local(file); //后台异步执行 backend.execute(compileLog, ymlStr); return Response.Builder .success() .setMsg("正在编译,可根据token查询编译结果") .appendData("token", token) .build(); } }
serviceImpl
@Component public class BackendService { private static final Logger log = LoggerFactory.getLogger(BackendService.class); @Autowired CompileRecordRepository recordRepository; @Value("${build.resource-dir}") String resourceDir; private final static String SCALA_FILE = ""; @Async public String execute(CompileLog compileLog, String ymlStr) { //模板代码目录 String capTemplateDir = resourceDir + "/cap"; String capInstanceDir = resourceDir + "/capInstance/" + compileLog.getToken(); FileUtil.mkdir(capInstanceDir); capInstanceDir += "/cap"; //创建实例代码目录 FileUtil.copyDir(capTemplateDir, capInstanceDir); log.info(Thread.currentThread().getName() + " start rendering..."); TemplateService.templateRender(ymlStr, capInstanceDir + SCALA_FILE); this.pack(capInstanceDir, compileLog); this.image(capInstanceDir, compileLog); log.info(Thread.currentThread().getName() + " update record..."); compileLog.setStatus(CompileStatus.SUCCESS); compileLog.setUpdateTime(System.currentTimeMillis()); recordRepository.save(compileLog); log.info(Thread.currentThread().getName() + " execute finished."); return "执行异步任务完毕"; } /** * 打成jar包 */ private void pack(String capDir, CompileLog record) { log.info("start package jar..."); } /** * 生成镜像 */ private void image(String capDir, CompileLog record) { log.info("start build docker image..."); } }
执行结果,略。。。。。。
很多情况下,异步处理是一种很常见而且高效的方式,springBoot自带的注解方式只用两个注解即可实现,简单易用。当然除此之外还有其他的实现方式,例如可以通过创建线程池来实现。
posted on 2018-08-31 13:53 taich-flute 阅读(841) 评论(0) 编辑 收藏 举报