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

拿自动排课来说,如果一个学校上百个班级,执行一次排课可能需要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);

}

Logo

前往低代码交流专区

更多推荐