【问题标题】:Best way to limit time execution in a @RestController在@RestController 中限制时间执行的最佳方法
【发布时间】:2019-02-05 12:04:38
【问题描述】:

考虑以下代码:

@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 spring-boot spring-restcontroller spring-async request-timed-out


    【解决方案1】:

    我设法做到了,但我不知道是否有任何概念或实践缺陷是它遵循的......

    一、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{ }
    

    【讨论】:

    • 这是一个不错的方法。但我更愿意使用像 Hystrix 这样的断路器来解决这个问题。在 hystrix 中,可以指定超时值。集成 Hystrix 相当简单
    【解决方案2】:

    通过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");时才会返回实际结果。

    如您所见,在超时的情况下(超时值在 DeferredResult 对象的构造函数中定义,以毫秒为单位)将执行一个回调,您可以在其中抛出任何异常,或返回另一个对象(HttpStatus.REQUEST_TIMEOUT 在你的情况下)。

    您可以在 Spring here 阅读有关 DeferredResult 的信息。

    【讨论】:

      猜你喜欢
      • 2019-02-05
      • 1970-01-01
      • 2018-01-25
      • 1970-01-01
      • 1970-01-01
      • 2015-12-04
      • 1970-01-01
      • 1970-01-01
      • 2021-06-02
      相关资源
      最近更新 更多