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

Posted

技术标签:

【中文标题】如何在 Spring Boot REST API 上设置超时?【英文标题】:How to set a timeout on a Spring Boot REST API? 【发布时间】:2019-06-02 11:42:44 【问题描述】:

我有一些 REST API 可能需要一段时间才能执行,我想限制它们的执行时间。最好,如果 30 秒过去了并且请求没有返回,我想返回一个特定的 HTTP 代码/数据并完全终止该请求。

当前代码:

@RestController
@CrossOrigin(origins = "*", maxAge = 4800, allowCredentials = "false")
public class APIController 

@RequestMapping(value = "/api/myapifunc", method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<?> optimize(@RequestParam(value="param1", defaultValue="")) 
    // Code here

【问题讨论】:

this post上的一些好答案 为什么不在您正在使用的服务上设置适当的超时和回滚,而不是在不知道中止此类请求的含义的情况下在控制器级别进行全面“杀死”? @jbx - 好问题。我们在该 REST 服务中使用了第三方,这可能需要很长时间才能执行。我对此没有任何控制权,因此我需要中止它以防执行时间过长,以确保此类事务不会导致服务器负载过高。 @jbs - 但你的问题让我想到,也许我们应该只限制该特定功能的执行,而不是整个控制器级别。谢谢你的评论,点赞! :) @TomShir 欢呼。如果您使用 Spring 的 RestTemplate,您实际上可以将其配置为超时。 docs.spring.io/spring-boot/docs/current/api/org/springframework/… 如果需要,还有连接超时。如果该服务通常很慢,那么您可能想看看您是否可以并行运行它与您可能执行的不需要它响应的任何其他操作。 【参考方案1】:
@RequestMapping(value = "/api/myapifunc", method = RequestMethod.POST, produces = 
"application/json")
public ResponseEntity<?> optimize(@RequestParam(value="param1", defaultValue="")) 
 return new Callable<String>() 
    @Override
    public String call() throws Exception 
        Thread.sleep(3000); //this will cause a timeout
        return "foobar";
    
  ;

未来你可以使用或注释 @Timed @Transactional(timeout = 3000)

【讨论】:

【参考方案2】:

您似乎在描述Circuit Breaker pattern。如果您可以控制客户端和服务器代码并想探索 Spring Cloud 和 Netflix Hysterix 库,您可以查看Getting Started: Circuit Breaker 指南。

如果您使用 Apache Tomcat 作为您的 servlet 容器,您可以配置 Stuck Thread Detection Valve:

此阀门允许检测需要很长时间才能处理的请求,这可能表明正在处理它的线程被卡住了。此外,它可以选择中断此类线程以尝试解除阻塞。

当检测到这样的请求时,其线程的当前堆栈跟踪将被写入具有 WARN 级别的 Tomcat 日志。

卡住线程的 ID 和名称可通过 JMX 在stdThreadIds 和stuckThreadNames 属性中获得。这些 ID 可以与标准 Threading JVM MBean (java.lang:type=Threading) 一起使用,以检索有关每个卡住线程的其他信息。

【讨论】:

【参考方案3】:

你可以设置这个属性配置

 server.connection-timeout=30000 

在您的 application.properties 中。 基于official documentation 说:

server.connection-timeout= # 连接器在关闭连接之前等待另一个 HTTP 请求的时间。如果未设置,则使用连接器的特定于容器的默认值。使用值 -1 表示没有(即无限)超时。

【讨论】:

不幸的是,这不起作用。无论如何,谢谢你的时间:) 这不是为了让正在进行的请求超时,而是为了让未接收请求的空闲连接超时。【参考方案4】:

使用 Spring Boot 2.3 / Tomcat 9,您可以通过安装 Tomcat StuckThreadDetectionValveALL 传入的 HTTP 请求设置超时。这是您需要的 Spring 配置代码(它是 Kotlin):

import org.apache.catalina.valves.StuckThreadDetectionValve
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class RequestTimeoutConfiguration(
    @Value("\$app.tomcat.stuck-thread-detection.request-timeout.seconds")
    private val stuckThreadTimeoutSeconds: Int
) 

    @Bean
    fun stuckThreadDetectionValve() =
        StuckThreadDetectionValve().apply 
            threshold = stuckThreadTimeoutSeconds
            interruptThreadThreshold = stuckThreadTimeoutSeconds
        

    @Bean
    fun stuckThreadDetectionWebServerFactoryCustomizer(valve: StuckThreadDetectionValve) =
        WebServerFactoryCustomizer  factory: TomcatServletWebServerFactory ->
            factory.addContextValves(valve)
        

那么你只需要application.properties中的属性来控制它:

app.tomcat.stuck-thread-detection.request-timeout.seconds=130

【讨论】:

这不适用于我们的请求堆积并且正在等待连接的情况。我不适应等待时间。

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

Spring Boot JWT - 如何实现刷新令牌和注销 REST-API

如何在 Spring Boot REST API 中格式化返回的 json?

在 Spring-boot 中成功登录后如何限制 POST Rest Api 以供公共访问

如何在 Spring Boot Rest api 响应的 ResponseEntity 中添加自定义属性

如何在 Spring Boot Rest API 中以 XML 形式返回对象列表

如何在 Spring Boot Rest api 中创建安全登录控制器