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 () -> /* 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-timeout
或 server.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-timeout
或 server.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=5000ms
或spring.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 获取请求中不起作用。如何解决?