Spring Boot REST API - 请求超时?

Posted

技术标签:

【中文标题】Spring Boot REST API - 请求超时?【英文标题】:Spring Boot REST API - request timeout? 【发布时间】:2016-04-23 11:26:51 【问题描述】:

我有一个 Spring Boot REST 服务,它有时会在请求中调用第三方服务。我想为我的所有资源设置一个超时时间(比如说 5 秒),这样如果任何请求处理(整个链,从传入到响应)花费的时间超过 5 秒,我的控制器就会使用 HTTP 503 而不是实际响应进行响应.如果这只是一个 Spring 属性,那就太棒了,例如设置

spring.mvc.async.request-timeout=5000

但我没有任何运气。我也尝试过扩展 WebMvcConfigurationSupport 并覆盖 configureAsyncSupport:

@Override
public void configureAsyncSupport(final AsyncSupportConfigurer configurer) 
    configurer.setDefaultTimeout(5000);
    configurer.registerCallableInterceptors(timeoutInterceptor());


@Bean
public TimeoutCallableProcessingInterceptor timeoutInterceptor() 
    return new TimeoutCallableProcessingInterceptor();

运气不好。

我怀疑我必须手动为所有第三方调用计时,如果调用时间过长,则抛出超时异常。那正确吗?或者是否有任何更简单、更全面的解决方案涵盖我的所有请求端点?

【问题讨论】:

【参考方案1】:

如果你想让spring.mvc.async.request-timeout=5000 工作,你需要返回一个Callable<>

@RequestMapping(method = RequestMethod.GET)
public Callable<String> getFoobar() throws InterruptedException 
    return new Callable<String>() 
        @Override
        public String call() throws Exception 
            Thread.sleep(8000); //this will cause a timeout
            return "foobar";
        
    ;

【讨论】:

如果使用Java 8,也可以使用lamba表达式:return () -&gt; /* do your stuff here*/;【参考方案2】:

@Transactional 注解带有一个超时参数,您可以在其中为@RestController 中的特定方法指定超时(以秒为单位)

@RequestMapping(value = "/method",
    method = RequestMethod.POST,
    produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
@Transactional(timeout = 120)

【讨论】:

有没有办法在 spring-boot 中全局设置事务超时? @Transactional 旨在处理数据库事务行为,而不是为 REST API 操作添加执行时间限制。【参考方案3】:

Spring Boot 2.2 需要一个全新的答案,因为 server.connection-timeout=5000 已弃用。每个服务器的行为都不同,因此建议使用特定于服务器的属性。

SpringBoot 默认嵌入 Tomcat,如果您没有使用 Jetty 或其他东西重新配置它。 使用特定于服务器的应用程序属性,例如 server.tomcat.connection-timeoutserver.jetty.idle-timeout

正如Wilkinson评论的那样:

设置连接超时只会导致超时 客户端连接但太慢而无法发送其请求。如果你想 客户端最多等待 30 秒的响应,您将 必须在客户端进行配置。如果你想要服务器端 最多只花费 30 秒处理请求没有 保证您不能强制正在处理的线程的方法 请求停止。

你也可以尝试设置spring.mvc.async.request-timeout

【讨论】:

我将所有 3 个用于使用 @RestController 的应用程序,并且我为它们中的每一个设置了非常低的值,但它们都被忽略了。即使请求需要更长的时间,页面也会正常提供。 这不会限制您的回答,而是限制客户端在打开连接后向您发送请求所需的时间:请参阅此处github.com/spring-projects/spring-boot/issues/21914 所以连接/空闲超时实际上是请求超时? ))【参考方案4】:

我建议您查看 Spring Cloud Netflix Hystrix 启动器,以处理可能不可靠/缓慢的远程调用。它实现了断路器模式,正是为这种事情而设计的。

见offcial docs for more information。

【讨论】:

我不明白为什么这被否决了,在我看来,实施断路器模式是最好的答案。否则,在保持请求运行的同时简单地返回 503,您可能会浪费资源 你也可以使用resilience4j resilience4j.readme.io/docs/circuitbreaker。实际的 Netflix Hystrix 不再维护。【参考方案5】:

您可以在 application.properties 中尝试server.connection-timeout=5000。来自official documentation:

server.connection-timeout= # 连接器将连接的时间(以毫秒为单位) 在关闭连接之前等待另一个 HTTP 请求。没有的时候 设置后,将使用连接器特定于容器的默认值。用一个 值为 -1 表示没有(即无限)超时。

另一方面,您可能希望使用断路器模式在客户端处理超时,正如我在此处的回答中已经描述的那样:https://***.com/a/44484579/2328781

【讨论】:

server.connection-timeout=5000 - 已弃用 - 每个服务器的行为不同。请改用服务器特定属性。 @Zon 你知道基本 Spring Boot 2 应用程序的服务器特定属性吗? @MozenRath,如果您没有使用 Jetty 或其他东西重新配置它,SpringBoot 默认嵌入 Tomcat。请改用服务器特定的应用程序属性,例如 server.tomcat.connection-timeoutserver.jetty.idle-timeout【参考方案6】:

如果您使用的是 RestTemplate,那么您应该使用以下代码来实现超时

@Bean
public RestTemplate restTemplate() 
    return new RestTemplate(clientHttpRequestFactory());


private ClientHttpRequestFactory clientHttpRequestFactory() 
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
    factory.setReadTimeout(2000);
    factory.setConnectTimeout(2000);
    return factory;

xml配置

<bean class="org.springframework.web.client.RestTemplate">
<constructor-arg>
    <bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory"
        p:readTimeout="2000"
        p:connectTimeout="2000" />
</constructor-arg>

【讨论】:

如果我只想为某些特定的 api 而不是所有服务使用超时怎么办 断路器方法会更好。见resilience4j.readme.io/docs/getting-started-3【参考方案7】:

在 Spring 属性文件中,您不能只为该属性指定一个数字。您还需要指定一个单位。所以你可以说spring.mvc.async.request-timeout=5000msspring.mvc.async.request-timeout=5s,两者都会给你一个5 秒的超时时间。

【讨论】:

您能详细说明一下吗?我试过了,没有单位就不行。 查看其他一些答案或官方文档 (docs.spring.io/spring-boot/docs/current/reference/html/…)。该问题与SpringBoot有关。 你的链接只是把我带到了源代码,它告诉我属性类型是java.time.Duration,它需要一个单位。我确实看过其他答案,但没有人讨论如何在属性文件中指定 spring.mvc.async.request-timeout 属性。他们都在代码中指定了它。据我所知,您链接到的文档支持我所说的。所以再一次。能详细点吗? 查看 Danylo Zatorsky 的回答。或互联网上的任何其他人:***.com/questions/47025525/… 或 ***.com/questions/51006913/… 所以你是说我说的是错误的属性?我的观点是,根据我的经验,返回 Duration 的属性需要指定单位。我只是以spring.mvc.async.request-timeout 属性为例,因为最初的提问者使用了它。你对这个说法有异议吗?还是您只是指要使用的正确属性。请自己回答我的问题,而不是把我指向其他地方。你的答案一直很模糊,直到最新的一个,这最终给了我一些更具体的东西。【参考方案8】:

我觉得没有一个答案能真正解决问题。我认为您需要告诉 Spring Boot 的嵌入式服务器处理请求的最长时间应该是多少。我们如何做到这一点取决于所使用的嵌入式服务器的类型。

如果是 Undertow,可以这样做:

@Component
class WebServerCustomizer : WebServerFactoryCustomizer<UndertowServletWebServerFactory> 
    override fun customize(factory: UndertowServletWebServerFactory) 
        factory.addBuilderCustomizers(UndertowBuilderCustomizer 
            it.setSocketOption(Options.READ_TIMEOUT, 5000)
            it.setSocketOption(Options.WRITE_TIMEOUT, 25000)
        )
    

Spring Boot 官方文档:https://docs.spring.io/spring-boot/docs/2.2.0.RELEASE/reference/html/howto.html#howto-configure-webserver

【讨论】:

【参考方案9】:

您可以为 Springboot REST 服务配置异步线程执行器。 setKeepAliveSeconds() 应该考虑请求链的执行时间。设置 ThreadPoolExecutor 的 keep-alive 秒数。默认值为 60。可以在运行时修改此设置,例如通过 JMX。

@Bean(name="asyncExec")
public Executor asyncExecutor()

    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(3);
    executor.setMaxPoolSize(3);
    executor.setQueueCapacity(10);
    executor.setThreadNamePrefix("AsynchThread-");
    executor.setAllowCoreThreadTimeOut(true);
    executor.setKeepAliveSeconds(10);
    executor.initialize();

    return executor;

然后您可以如下定义您的 REST 端点

@Async("asyncExec")
@PostMapping("/delayedService")
public CompletableFuture<String> doDelay()
 
    String response = service.callDelayedService();
    return CompletableFuture.completedFuture(response);

【讨论】:

以上是关于Spring Boot REST API - 请求超时?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我登录后无法进入管理 REST API 请求映射 - Spring Boot

REST API 在 Spring Boot 中删除 HTTP 请求

在 N 个并行保存请求后 Spring Boot Rest Api 卡住了

如何使用 Postman 表单数据将 POST 请求发送到 Spring Boot REST API?

如何在 Spring Boot REST API 上设置超时?

ControllerAdvice 的异常处理程序在使用 Spring Boot 的 Rest API 获取请求中不起作用。如何解决?