在项目中会遇到这样的情况,由于后台需要执行、计算一段时间(如计算积分、自动排课等)。这时前台请求一段时间后,得不到返回结果就会发生请求超时。

拿自动排课来说,如果一个学校上百个班级,执行一次排课可能需要1-2分钟甚至更长时间,那么会造成前台访问接口超时(当然也可以延长超时时间实现)。

解决方案:

考虑改造为轮询查询程序执行结果。

1. 后台改造:

将自动排课功能的接口分为两个:

①创建线程执行自动排课

②提供接口查询排课结果

对原有的方法进行改造:原有的方法,方法执行完成后才会返回执行结果。时间过长,考虑将原方法改造为线程执行,这样一旦线程开始执行,就可以返回结果。

改造方法:

自动排课功能所在的service类实现Runnable接口,将自动排课的实现逻辑写在run方法中。

编写方法①创建并执行线程,执行run方法。

Controller层调用方法①实现自动排课功能。

对于自动排课结果,可以放在redis中,接口①实时更新自动排课的状态(成功或者失败),可以通过接口②每间隔一段时间查询自动排课的结果。

代码示例:

Controller层
@Controller
public class Controller{
@Autowired
//自动注入排课功能所在的service
private CourseTableService courseTableService;
@RequestMapping(value = "/arrange/{id}")
@ResponseBody
public ResponseMessage arrange(@PathVariable String id) {
return courseTableService.arrange(id); //自动排课
}
@RequestMapping(value = "/arrangeResult/{id}")
@ResponseBody
public Map arrangeResult(@PathVariable String id) {
//查询自动排课结果,并返回
}
}
Service层
@Service
@Transactional(readOnly = true)
public class CourseTableService implements Runnable { //实现Runnable接口
@Autowired
private ThreadPoolTaskExecutor taskExecutor; //线程池
//自动paike
public ResponseMessage arrange(String scheduleId) {
this.scheduleId=scheduleId; //设置run方法中需要用的参数
taskExecutor.execute(this); //执行线程
return ResponseMessage.ok(); //返回线程执行结果
}
//自动排课,线程
public void run(){
//排课逻辑代码
String scheId=this.scheduleId; //使用接收的参数
}
}

2. 前端大致分两次请求后台接口:

第一次请求接口自动排课(线程或者mQ执行),这样在启动自动排课的时候就返回请求结果,告知用户正在进行排课。

然后轮询调用第二接口,每隔几秒钟就去查询排课的结果。如果返回的状态为0代表排课成功,提示用户;如果返回的状态为1达标排课失败,提示失败原因;如果返回的状态为2代表排课正在执行中,继续轮询访问查询排课结果的接口。

主要代码示例:

var intervalFlag=true; //是否执行轮询的标志
//_post2是封装的ajax请求,
$._post2('/arrange/' + _id, {}, function(res) {
var interVal;
//调用接口,查询自动排课结果,加上这个是为了用户点击后立马访问,ajax同步访问,
//因为这次的查询结果决定了,是否执行轮询。
getProgress(_id, interVal);
if(intervalFlag){
//第一次查询结果表明排课还在进行中,才会执行轮询。
//如果第一次已经返回结果表示程序执行完成,就不需要轮询访问排课结果了。
interval(_id);
}
});
// 进度查询
function getProgress(_id, interVal) {
$._post2('/arrangeResult/' + _id, {}, function(res) {
if (res.arrangeStatus == 0) {
//排课成功
clearInterval(interVal); //清空轮询
intervalFlag=false; //设置为不执行轮询
}else if(res.arrangeStatus==1){
//排课失败
clearInterval(interVal);
intervalFlag=false;
}else if(res.arrangeStatus==2){
//排课进行中,什么都不做
}
});
}
// 隔两秒访问
function interval(_id) {
var pro;
// 定时器
var interVal;
interVal = setInterval(function() {
// 获取返回对象
pro = getProgress(_id, interVal);
}, 2000);
}