我有一个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();
}
没有任何运气。
我怀疑我必须手动计时所有第三方调用,如果它们花费的时间太长,请抛出超时异常。是吗?或者是否有任何更简单的整体解决方案可以涵盖我的所有请求端点?
如果您希望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";
}
};
}
Spring Boot 2.2 需要一个新的答案,因为server.connection-timeout=5000
已被弃用。每个服务器的行为不同,因此建议改为使用特定于服务器的属性。
默认情况下,SpringBoot嵌入了Tomcat,如果你没有用Jetty或其他东西重新配置它。使用特定于服务器的应用程序属性,如 server.tomcat.connection-timeout
或 server.jetty.idle-timeout
。
正如威尔金森评论的那样:
设置连接超时只会导致超时,当客户端连接,但速度太慢,无法发送其请求。如果需要帮助,客户端最多等待 30 秒的响应,您将必须在客户端进行配置。如果你想要服务器端最多只花费 30 秒来处理请求,则没有保证的方法,因为你不能强制正在处理的线程停止请求。
您也可以尝试设置spring.mvc.async.request-timeout
@Transactional
注释采用超时参数,您可以在该参数中为@RestController
中的特定方法指定超时(以秒为单位)
@RequestMapping(value = "/method",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
@Transactional(timeout = 120)
我建议你看看Spring Cloud Netflix Hystrix入门,以处理可能不可靠/缓慢的远程呼叫。它实现了断路器模式,该模式正是针对此类事情的。
有关详细信息,请参阅官方文档。
您可以在 application.properties 中尝试server.connection-timeout=5000
。来自官方文档:
server.connection-timeout= # 连接器将花费的时间(以毫秒为单位) 在关闭连接之前等待另一个 HTTP 请求。当不是 设置后,将使用连接器特定于容器的默认值。使用 值为 -1 表示无(即无限)超时。
另一方面,您可能希望使用断路器模式处理客户端的超时,正如我在此处的回答中已经描述的那样:https://stackoverflow.com/a/44484579/2328781
如果您使用的是 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>
在 Spring 属性文件中,您不能只为此属性指定一个数字。您还需要指定单位。所以你可以说 spring.mvc.async.request-timeout=5000ms
或spring.mvc.async.request-timeout=5s
,两者都会给你 5 秒的超时。
没有一个答案能真正解决问题。我认为您需要告诉Spring Boot的嵌入式服务器处理请求的最长时间应该是多少。我们究竟如何做到这一点取决于所使用的嵌入式服务器的类型。
在暗流的情况下,可以这样做:
@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)
})
}
}
春季启动官方文档:https://docs.spring.io/spring-boot/docs/2.2.0.RELEASE/reference/html/howto.html#howto-configure-webserver
您可以为 Springboot REST 服务配置异步线程执行器。setKeepAliveSeconds() 应该考虑请求链的执行时间。设置 ThreadPoolExecutor 的保持活动秒数。默认值为 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);
}