考虑以下代码:
@RestController
@RequestMapping("/timeout")
public class TestController {
@Autowired
private TestService service;
@GetMapping("/max10secs")
public String max10secs() {
//In some cases it can take more than 10 seconds
return service.call();
}
}
@Service
public class TestService {
public String call() {
//some business logic here
return response;
}
}
我想完成的是,如果call
中的方法TestService
花费了10秒钟以上,我想取消它并生成一个带有HttpStatus.REQUEST_TIMEOUT
代码的响应。
答案 0 :(得分:4)
我设法做到了,但是我不知道接下来有什么概念或实践上的缺陷...
首先,配置spring-async
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
pool.setCorePoolSize(10);
pool.setMaxPoolSize(10);
pool.setWaitForTasksToCompleteOnShutdown(true);
return pool;
}
@Override
public Executor getAsyncExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
接下来,对Controller和Service进行修改:
@RestController
@RequestMapping("/timeout")
public class TestController {
@Autowired
private TestService service;
@GetMapping("/max10secs")
public String max10secs() throws InterruptedException, ExecutionException {
Future<String> futureResponse = service.call();
try {
//gives 10 seconds to finish the methods execution
return futureResponse.get(10, TimeUnit.SECONDS);
} catch (TimeoutException te) {
//in case it takes longer we cancel the request and check if the method is not done
if (futureResponse.cancel(true) || !futureResponse.isDone())
throw new TestTimeoutException();
else {
return futureResponse.get();
}
}
}
}
@Service
public class TestService {
@Async("threadPoolTaskExecutor")
public Future<String> call() {
try{
//some business logic here
return new AsyncResult<>(response);
} catch (Exception e) {
//some cancel/rollback logic when the request is cancelled
return null;
}
}
}
最后生成TestTimeoutException:
@ResponseStatus(value = HttpStatus.REQUEST_TIMEOUT, reason = "too much time")
public class TestTimeoutException extends RuntimeException{ }
答案 1 :(得分:0)
通过html.escape()还有另一种解决方案。
TestController.java
@RestController
@RequestMapping("/timeout")
public class TestController
{
@Autowired
private TestService service;
@GetMapping("/max10secs")
public DeferredResult<String> max10secs()
{
//In some cases it can take more than 10 seconds
return service.call();
}
}
TestService.java
@Service
public class TestService
{
public DeferredResult<String> call()
{
DeferredResult<String> result = new DeferredResult(10000L);
//some business logic here
result.onTimeout(()->{
// do whatever you want there
});
result.setResult("test");
return result;
}
}
这样,仅当您调用 result.setResult(“ test”); 时,控制器才会返回实际结果。
如您所见,如果发生超时(超时值在DeferredResult对象的构造函数中定义,以毫秒为单位),将执行一个回调,您可以在其中引发任何异常或返回另一个对象( HttpStatus.REQUEST_TIMEOUT) )。
您可以在春季DeferredResult中阅读有关DeferredResult的信息。