在@RestController 中限制执行时间的最佳方法
Best way to limit time execution in a @RestController
考虑以下代码:
@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;
}
}
我想要完成的是,如果 TestService
中的方法 call
花费的时间超过 10 秒,我想取消它并使用 HttpStatus.REQUEST_TIMEOUT
代码生成响应。
我设法做到了,但我不知道是否有任何概念或实践上的缺陷是这样的...
一、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();
}
}
接下来,控制器和服务修改:
@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{ }
DeferredResult还有另一个解决方案。
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"); 时,控制器才会 return 实际结果。
如您所见,如果超时(超时值在 DeferredResult 对象的构造函数中以毫秒为单位定义),将执行一个回调,您可以在其中抛出任何异常,或 return 另一个对象( HttpStatus.REQUEST_TIMEOUT 你的情况)。
您可以在 Spring here.
中阅读有关 DeferredResult 的信息
考虑以下代码:
@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;
}
}
我想要完成的是,如果 TestService
中的方法 call
花费的时间超过 10 秒,我想取消它并使用 HttpStatus.REQUEST_TIMEOUT
代码生成响应。
我设法做到了,但我不知道是否有任何概念或实践上的缺陷是这样的...
一、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();
}
}
接下来,控制器和服务修改:
@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{ }
DeferredResult还有另一个解决方案。
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"); 时,控制器才会 return 实际结果。
如您所见,如果超时(超时值在 DeferredResult 对象的构造函数中以毫秒为单位定义),将执行一个回调,您可以在其中抛出任何异常,或 return 另一个对象( HttpStatus.REQUEST_TIMEOUT 你的情况)。
您可以在 Spring here.
中阅读有关 DeferredResult 的信息