最近做一个项目,需要把excel导入到数据库中,一般的流程是后台poi解析excel,后台做校验,数据补全等逻辑再转换为json数据,返回到前台,前台点击确认导入后,再保存到后台。但是这样做有几个问题? 假设数据量很大或者校验,数据补全等特别耗时间,夸张的,甚至得几十分钟,这个请求可能就会被浏览器认为是超时请求,有的同学可能会说,小意思,我把浏览器超时时间设置的长一点不就好了,这个办法没有根本的解决问题,因为不可能要求每个用户都去改浏览器超时时间,即使可以,一般nginx反向代理也有超时设置,总不能把这个时间也改的很大吧,第二个问题?如何能保证用户保存的json数据是服务器返回给它的呢?懂前端的用户可能会把json给改了,但是一般我们不想这样,因为假设是一些特别重要的数据,这些数据只能根据他导入的excel得到,我们可能会把他上传的excel给保存起来,甚至还需要需要做做数字签名。第三个问题?如果用户量很大的话,每个请求占用时间太长,最终会导致线程池被耗尽,服务器无法响应。既然有如此多的问题,我们该如何解决呢,我有个解决思路,异步请求+redis,大题思路是这样,用户上传的excel后,我异步(开个新线程)的去执行excel解析,等解析完,把结果存储到redis里面,当开启线程后,我直接给浏览器返回一个uuid, 不必等线程执行完,才返回,最后浏览器拿着这个uuid轮询的去redis里面取结果。
下面是这种想法的简单实现:
1用到的技术有:JSR-311+ssj框架
2假设添加一个Person的操作非常耗时
package com.mochasoft.bjlt.controller; import com.mochasoft.bjlt.cucpm.core.liantong.service.IPersonService; import com.mochasoft.bjlt.cucpm.utils.Tools; import com.mochasoft.data.resteasy.core.CustomMediaType; import com.mochasoft.latte.commons.utils.R; import com.mochasoft.latte.data.redis.repository.RedisRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import javax.ws.rs.*; @Controller @Path(value = "/") @Slf4j public class PersonController { @Autowired private IPersonService personService; @Autowired private RedisRepository redisRepository; /** * 增加人员信息 * @return */ @POST @Path("person") @Consumes("application/json") @Produces(value = CustomMediaType.APPLICATION_JSON_UTF_8) public R add(Person person) { try { String uuid = Tools.uuid(); //此方法为异步方法,加@Async注解,并把结果存储到redis中 personService.save(person); redisRepository.setEX(uuid,60*30,R.error(301, "创建人员信息未完成")); return R.ok().put("data",uuid); } catch (Exception e) { log.error("异步创建人员信息异常",e); return R.error(); } } @GET @Path("personInRedis") @Produces(value = CustomMediaType.APPLICATION_JSON_UTF_8) public R queryPersonInRedis(@QueryParam("key") String key) { try { return (R) redisRepository.get(key); } catch (Exception e) { log.error("查询redis里的人员信息异常",e); return R.error(); } } }