Spring Boot + tomcat 8.5 + mongoDB,AsyncRequestTimeoutException

Sac*_*141 10 java spring mongodb spring-boot tomcat8.5

我已经创建了一个Spring Boot Web应用程序,并将相同的战争部署到了tomcat容器。应用程序mongoDB使用异步连接进行连接。我正在mongodb-driver-async为此使用图书馆。

在启动时,一切正常。但是,一旦负载增加,它就会在数据库连接中显示以下异常:

org.springframework.web.context.request.async.AsyncRequestTimeoutException: null
        at org.springframework.web.context.request.async.TimeoutDeferredResultProcessingInterceptor.handleTimeout(TimeoutDeferredResultProcessingInterceptor.java:42)
        at org.springframework.web.context.request.async.DeferredResultInterceptorChain.triggerAfterTimeout(DeferredResultInterceptorChain.java:75)
        at org.springframework.web.context.request.async.WebAsyncManager$5.run(WebAsyncManager.java:392)
        at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.onTimeout(StandardServletAsyncWebRequest.java:143)
        at org.apache.catalina.core.AsyncListenerWrapper.fireOnTimeout(AsyncListenerWrapper.java:44)
        at org.apache.catalina.core.AsyncContextImpl.timeout(AsyncContextImpl.java:131)
        at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:157)
Run Code Online (Sandbox Code Playgroud)

我正在使用以下版本的软件:

  1. 春季启动-> 1.5.4.RELEASE
  2. Tomcat(作为独立的二进制文件安装)-> apache-tomcat-8.5.37
  3. Mongo DB版本:v3.4.10
  4. mongodb-driver-async:3.4.2

一旦我重新启动了tomcat服务,一切都会开始正常运行。

请帮助,这可能是此问题的根本原因。

PS:我正在使用DeferredResultCompletableFuture创建异步REST API。

我也尝试spring.mvc.async.request-timeout在应用程序中使用并asynTimeout 在tomcat中进行配置。但是仍然出现相同的错误。

Wil*_*urn 5

很明显,Spring 使您的请求超时并抛出AsyncRequestTimeoutException,这会将 503 返回给您的客户端。

现在的问题是,为什么会发生这种情况?有两种可能性。

  1. 这些都是合法的超时。您提到只有当服务器负载增加时您才会看到异常。因此,您的服务器可能无法处理该负载,并且其性能已经下降到某些请求在 Spring 超时之前无法完成的程度。

  2. 超时是由于您的服务器由于编程错误而未能发送对异步请求的响应,导致请求保持打开状态,直到 Spring 最终超时。如果你的服务器不能很好地处理异常,就很容易发生这种情况。如果您的服务器是同步的,则异常处理有点马虎是可以的,因为未处理的异常将传播到服务器框架,服务器框架会将响应发送回客户端。但是,如果您无法处理某些异步代码中的异常,则该异常将在其他地方(可能在某些线程池管理代码中)被捕获,并且该代码无法知道有一个异步请求正在等待操作的结果这引发了异常。

如果不了解有关您的应用程序的更多信息,就很难弄清楚可能会发生什么。但有些事情你可以调查一下。

首先,尝试查找资源耗尽情况。

  • 垃圾收集器是否一直在运行?
  • 所有 CPU 都固定为 100% 吗?
  • 操作系统交换频繁吗?
  • 如果数据库服务器位于单独的计算机上,该计算机是否显示资源耗尽的迹象?
  • 有多少个数据库连接打开?如果有连接池,连接池是否已满?
  • 有多少线程正在运行?如果服务器中有线程池,它们是否已满?

如果某些东西达到了极限,那么可能是瓶颈导致您的请求超时。

尝试设置spring.mvc.async.request-timeout为-1,看看会发生什么。您现在是否会缓慢地收到每个请求的响应,或者某些请求似乎永远挂起?如果是后者,则强烈表明您的服务器中存在错误,导致其无法跟踪请求并无法发送响应。(如果设置spring.mvc.async.request-timeout似乎没有效果,那么您接下来应该调查的是您用于设置配置的机制是否确实有效。)

我发现在这些情况下有用的策略是为每个请求生成一个唯一的 ID,并在每次服务器进行异步调用或从异步调用接收响应时以及在不同的时间写入该 ID 以及一些上下文信息。异步处理程序中的检查点。如果请求丢失,您可以使用日志信息来找出请求 ID 以及服务器上次对该请求执行的操作。

类似的策略是将每个请求 ID 保存到一个映射中,其中值是一个对象,用于跟踪请求何时启动以及服务器上次对该请求执行的操作。(在这种情况下,您的服务器会在每个检查点更新此映射,而不是写入日志。)您可以设置一个过滤器来生成请求 ID 并维护映射。如果您的过滤器发现服务器发送 5xx 响应,您可以从地图记录该请求的最后操作。

希望这可以帮助!

  • +1 表示使用唯一 ID 的策略。具有异步调用的多线程/应用程序总是很难调试,因此很难识别问题区域/根本原因 (2认同)