Quarkus应用读取InputStream触发BlockingNotAllowedException的解决咨询
问题描述
使用Quarkus 3.8.6.redhat-00005开发Java应用时,在标注@PreMatching的JAX-RS请求过滤器中,调用containerRequestContext.getEntityStream().readAllBytes()读取请求体,抛出BlockingNotAllowedException,错误提示为Attempting a blocking read on io thread。尝试在端点、Application子类及过滤器本身添加@Blocking注解,未解决问题。
用户原代码:
package br.com.bb.t99.rest; import io.smallrye.common.annotation.Blocking; import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServerRequest; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.PreMatching; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.ext.Provider; import java.io.IOException; @Provider @PreMatching public class TesteFilter implements ContainerRequestFilter { @Context HttpServerRequest request; @Override public void filter(ContainerRequestContext containerRequestContext) throws IOException { containerRequestContext.getEntityStream().readAllBytes(); //TODO: Edit the request body } }
错误堆栈核心信息:
org.jboss.resteasy.reactive.common.core.BlockingNotAllowedException: Attempting a blocking read on io thread at org.jboss.resteasy.reactive.server.vertx.VertxInputStream$VertxBlockingInput.readBlocking(VertxInputStream.java:245) at br.com.bb.t99.rest.TesteFilter.filter(TesteFilter.java:25)
核心原因
@PreMatching修饰的过滤器默认在Vertx IO线程执行,而InputStream.readAllBytes()是阻塞IO操作,Quarkus禁止在IO线程执行阻塞操作以保证性能。普通的@Blocking注解对@PreMatching过滤器不生效,因为这类过滤器的执行时机早于常规资源方法,默认不会被调度到阻塞线程池。
解决方案
方案一:阻塞方式(简单易用)
通过配置或修改过滤器类型,让过滤器在阻塞线程池执行,从而允许阻塞读取请求体。
方法1:配置全局开关
在application.properties中添加以下配置,让所有@PreMatching过滤器默认使用阻塞线程:
quarkus.resteasy-reactive.pre-matching-filters-blocking=true
之后在过滤器类上添加@Blocking注解即可:
@Provider @PreMatching @Blocking public class TesteFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext containerRequestContext) throws IOException { byte[] body = containerRequestContext.getEntityStream().readAllBytes(); // 修改请求体 containerRequestContext.setEntityStream(new ByteArrayInputStream("modified content".getBytes())); } }
方法2:改用异步过滤器
实现AsyncContainerRequestFilter接口,配合@Blocking注解,强制过滤器在阻塞线程执行:
@Provider @PreMatching @Blocking public class TesteFilter implements AsyncContainerRequestFilter { @Override public void filter(ContainerRequestContext containerRequestContext, AsyncResponse asyncResponse) throws IOException { byte[] body = containerRequestContext.getEntityStream().readAllBytes(); // 修改请求体逻辑 ByteArrayInputStream newStream = new ByteArrayInputStream(("processed: " + new String(body)).getBytes()); containerRequestContext.setEntityStream(newStream); // 恢复请求处理流程 asyncResponse.resume(); } }
方案二:非阻塞方式(高性能推荐)
利用Vertx的异步API读取请求体,全程不阻塞IO线程,适合高并发场景。需要结合AsyncContainerRequestFilter处理异步流程:
@Provider @PreMatching public class TesteFilter implements AsyncContainerRequestFilter { @Context HttpServerRequest request; @Override public void filter(ContainerRequestContext containerRequestContext, AsyncResponse asyncResponse) { // 异步读取请求体 request.body() .onSuccess(buffer -> { try { // 获取原请求体内容 String originalBody = buffer.toString(); // 修改请求体 String modifiedBody = originalBody + " [filtered]"; // 将修改后的内容设置回请求上下文 containerRequestContext.setEntityStream( new ByteArrayInputStream(modifiedBody.getBytes()) ); // 继续处理请求 asyncResponse.resume(); } catch (Exception e) { asyncResponse.resume(e); } }) .onFailure(asyncResponse::resume); // 处理读取失败的情况 } }
总结
- 若对性能要求不高,优先选择阻塞方式,配置简单、代码改动小;
- 若需处理高并发请求,推荐非阻塞方式,利用Vertx异步API避免IO线程阻塞。
内容的提问来源于stack exchange,提问作者Alexandre Queiroz de Oliveira

