Spring boot + tomcat 8.5 + mongoDB,AsyncRequestTimeoutException
Posted
技术标签:
【中文标题】Spring boot + tomcat 8.5 + mongoDB,AsyncRequestTimeoutException【英文标题】:Spring boot + tomcat 8.5 + mongoDB, AsyncRequestTimeoutException 【发布时间】:2019-10-25 19:21:46 【问题描述】:我创建了一个 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)
我正在使用以下版本的软件:
-
春季启动 -> 1.5.4.RELEASE
Tomcat(作为独立二进制安装)-> apache-tomcat-8.5.37
Mongo DB 版本:v3.4.10
mongodb-driver-async: 3.4.2
一旦我重新启动 tomcat 服务,一切都会开始正常工作。
请帮忙,这个问题的根本原因可能是什么。
P.S.:我正在使用 DeferredResult
和 CompletableFuture 来创建异步 REST API。
我也尝试在应用程序中使用spring.mvc.async.request-timeout
并在tomcat 中配置asynTimeout
。但仍然出现同样的错误。
【问题讨论】:
您可以尝试将 spring.mvc.async.request-timeout 设置为较高的值,看看是否有帮助。你知道哪个手术需要很长时间吗? 是的,我已经试过了,但是没有用 您知道是哪些操作导致了问题吗? 在所有操作中 这可能与此线程有关吗? ***.com/questions/39856198/… 【参考方案1】:很明显,Spring 正在超时您的请求并抛出 AsyncRequestTimeoutException
,这会将 503 返回给您的客户端。
现在的问题是,为什么会发生这种情况?有两种可能。
这些是合法的超时。您提到只有在服务器负载增加时才会看到异常。因此,您的服务器可能无法处理该负载,并且其性能已经下降到某些请求在 Spring 超时之前无法完成的地步。
超时是由于您的服务器由于编程错误而未能发送对异步请求的响应,从而使请求保持打开状态,直到 Spring 最终超时。如果您的服务器不能很好地处理异常,则很容易发生这种情况。如果您的服务器是同步的,那么在异常处理方面稍微马虎一点也没关系,因为未处理的异常会传播到服务器框架,服务器框架会将响应发送回客户端。但是如果在某些异步代码中处理异常失败,该异常将在其他地方被捕获(可能在某些线程池管理代码中),并且该代码无法知道有一个异步请求在等待操作结果引发了异常。
如果不了解您的应用程序的更多信息,很难弄清楚可能会发生什么。但有些事情你可以调查。
首先,尝试寻找资源耗尽。
垃圾收集器是否一直在运行? 是否所有 CPU 都固定为 100%? 操作系统是否频繁交换? 如果数据库服务器位于单独的机器上,该机器是否显示资源耗尽迹象? 数据库打开了多少连接?如果有连接池,是否已满? 有多少线程正在运行?如果服务器中存在线程池,它们是否已满?如果某事处于其极限,那么可能是导致您的请求超时的瓶颈。
尝试将spring.mvc.async.request-timeout
设置为-1,看看会发生什么。您现在是否收到每个请求的响应,只是很慢,还是有些请求似乎永远挂起?如果是后者,则强烈表明您的服务器中存在错误,导致它无法跟踪请求并无法发送响应。 (如果设置 spring.mvc.async.request-timeout
似乎没有任何效果,那么您接下来应该调查的是您用于设置配置的机制是否真的有效。)
我发现在这些情况下有用的一个策略是为每个请求生成一个唯一的 ID,并在每次服务器进行异步调用或接收到来自异步调用的响应时将 ID 连同一些上下文信息一起写入,以及异步处理程序中的各个检查点。如果请求丢失,您可以使用日志信息来确定请求 ID 以及服务器上次对该请求执行的操作。
类似的策略是将每个请求 ID 保存到一个映射中,其中的值是一个对象,用于跟踪请求何时开始以及您的服务器最后对该请求做了什么。 (在这种情况下,您的服务器会在每个检查点更新此映射,而不是写入日志或除此之外。)您可以设置过滤器来生成请求 ID 并维护映射。如果您的过滤器看到服务器发送 5xx 响应,您可以从地图记录该请求的最后一个操作。
希望这会有所帮助!
【讨论】:
+1 用于使用唯一 ID 的策略。带有异步调用的多线程/应用程序总是很难调试,因此很难确定问题区域/根本原因 感谢您将此标记为正确答案!您发现问题是什么?【参考方案2】:异步任务排列在队列(池)中,队列(池)根据分配的线程数并行处理。并非所有异步任务都同时执行。其中一些正在排队。在这样的系统中,获得 AsyncRequestTimeoutException 是正常行为。
如果您正在用无法在压力下执行的异步任务填满队列。增加超时只会延迟问题。你应该专注于这个问题:
-
减少异步任务的执行时间(通过各种优化)。这将放松异步任务的池化。它显然需要编码。
增加分配的 CPUS 数量,以便能够更高效地运行并行任务。
增加为驱动程序的执行程序服务的线程数。
如果在类路径中找到 Netty,Mongo 异步驱动程序正在使用 AsynchronousSocketChannel
或 Netty。为了增加为异步通信提供服务的工作线程的数量,您应该使用:
MongoClientSettings.builder()
.streamFactoryFactory(NettyStreamFactoryFactory(io.netty.channel.EventLoopGroup eventLoopGroup,
io.netty.buffer.ByteBufAllocator allocator))
.build();
其中 eventLoopGroup 是 io.netty.channel.nio.NioEventLoopGroup(int nThreads))
在 NioEventLoopGroup 上,您可以设置为异步通信服务的线程数
在此处阅读有关 Netty 配置的更多信息https://mongodb.github.io/mongo-java-driver/3.2/driver-async/reference/connecting/connection-settings/
【讨论】:
以上是关于Spring boot + tomcat 8.5 + mongoDB,AsyncRequestTimeoutException的主要内容,如果未能解决你的问题,请参考以下文章
已为此响应调用 Tomcat 8.5 response.getWriter()
Spring Boot:当 spring-boot-starter-tomcat 和 tomcat-embed-jasper 一起时“扫描失败”
spring-boot-starter-tomcat 与 spring-boot-starter-web