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自带的注解方式只用两个注解即可实现,简单易用。当然除此之外还有其他的实现方式,例如可以通过创建线程池来实现。

猜你喜欢

转载自www.cnblogs.com/taich-flute/p/9565423.html