You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus应用读取InputStream触发BlockingNotAllowedException的解决咨询

解决Quarkus请求过滤器读取请求体的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:34:54