Quarkus Vertx构建后首次7MB流API调用连接超时问题
首次大流量(7MB)API调用失败,后续正常的排查与解决
问题现象
当请求输入流大小为7MB时,服务构建完成后的首次API调用必定失败,后续所有调用均可正常执行。失败时抛出连接关闭异常,完整栈日志如下:
2023-12-18 23:18:11,768 WARNING 23 [com.log.cor.jax.imp.exc.map.BaseExceptionMapper] (50740c4dfeb9df41) createResponse(Throwable): java.io.IOException: java.io.IOException: io.vertx.core.http.HttpClosedException: Connection was closed at io.quarkus.vertx.http.runtime.VertxInputStream$VertxBlockingInput.readBlocking(VertxInputStream.java:255) at io.quarkus.vertx.http.runtime.VertxInputStream.readIntoBuffer(VertxInputStream.java:122) at io.quarkus.vertx.http.runtime.VertxInputStream.read(VertxInputStream.java:84) at java.base/java.io.SequenceInputStream.read(SequenceInputStream.java:199) at org.apache.james.mime4j.io.BufferedLineReaderInputStream.fillBuffer(BufferedLineReaderInputStream.java:111) at org.apache.james.mime4j.io.MimeBoundaryInputStream.fillBuffer(MimeBoundaryInputStream.java:223) at org.apache.james.mime4j.io.MimeBoundaryInputStream.read(MimeBoundaryInputStream.java:157) at org.apache.james.mime4j.io.BufferedLineReaderInputStream.fillBuffer(BufferedLineReaderInputStream.java:111) at org.apache.james.mime4j.io.BufferedLineReaderInputStream.read(BufferedLineReaderInputStream.java:158) at org.apache.james.mime4j.io.LineReaderInputStreamAdaptor.read(LineReaderInputStreamAdaptor.java:67) at java.base/java.io.FilterInputStream.read(FilterInputStream.java:107) at org.apache.james.mime4j.util.ContentUtil.copy(ContentUtil.java:55) at org.apache.james.mime4j.storage.AbstractStorageProvider.store(AbstractStorageProvider.java:57) at org.apache.james.mime4j.storage.StorageBodyFactory.binaryBody(StorageBodyFactory.java:94) at org.jboss.resteasy.plugins.providers.multipart.Mime4jWorkaroundBinaryEntityBuilder.body(Mime4jWorkaroundBinaryEntityBuilder.java:159) at org.apache.james.mime4j.parser.MimeStreamParser.parse(MimeStreamParser.java:133) at org.jboss.resteasy.plugins.providers.multipart.Mime4JWorkaround.parseMessage(Mime4JWorkaround.java:83) at org.jboss.resteasy.plugins.providers.multipart.MultipartInputImpl.parse(MultipartInputImpl.java:104) at org.jboss.resteasy.plugins.providers.multipart.MultipartFormAnnotationReader.readFrom(MultipartFormAnnotationReader.java:58) at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.readFrom(AbstractReaderInterceptorContext.java:101) at org.jboss.resteasy.core.interception.jaxrs.ServerReaderInterceptorContext.readFrom(ServerReaderInterceptorContext.java:63) at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:80) at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:213) at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:95) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:128) at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524) at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:408) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:69) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364) at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247) at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:82) at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:147) at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:93) at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:576) at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478) at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29) at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:829) Caused by: java.io.IOException: io.vertx.core.http.HttpClosedException: Connection was closed
已尝试调整Vertx相关配置,但问题未解决。
可能原因分析
- 组件预热不足:Quarkus+Vertx架构下,首次处理大流量请求时,HTTP连接池、请求缓冲区、Multipart解析器等组件未完成初始化,导致读取请求体过程中连接被提前关闭
- 超时配置不匹配:默认的请求体读取超时时间过短,首次调用时组件初始化耗时叠加请求处理时间,触发超时
- Multipart解析器初始化延迟:日志中涉及的Mime4J解析组件首次加载时,类初始化、资源准备耗时过长,导致连接在解析完成前被关闭
解决方案
1. 添加服务启动预热逻辑
在服务启动完成后,主动发起一个小流量请求,触发所有核心组件初始化:
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.event.Observes; import io.quarkus.runtime.StartupEvent; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; @ApplicationScoped public class ApiWarmer { void onStartup(@Observes StartupEvent event) { // 发起简单的预热请求,路径替换为你的健康检查或轻量接口 Client client = ClientBuilder.newClient(); client.target("http://localhost:8080/health").request().get(); client.close(); } }
2. 调整Vertx HTTP核心配置
在application.properties中添加以下配置,延长超时时间并优化缓冲区:
# 延长请求体读取超时时间 quarkus.vertx.http.read-timeout=30s # 增大请求体大小限制 quarkus.vertx.http.body-limit=32MB # 调整HTTP块大小,适配大流量请求 quarkus.vertx.http.max-chunk-size=16MB # 优化连接池参数,加快预热速度 quarkus.vertx.http.pool.max-size=20 quarkus.vertx.http.pool.cleaner-period=5s
3. 提前加载核心解析类
配置Quarkus提前加载Multipart相关解析类,避免首次调用时的初始化延迟:
quarkus.arc.preloaded-classes=org.jboss.resteasy.plugins.providers.multipart.Mime4JWorkaround,org.apache.james.mime4j.parser.MimeStreamParser
内容的提问来源于stack exchange,提问作者Akanksha Singh
相关产品推荐
相关产品推荐

