从 InputStream 读取时 Quarkus 应用程序中出现 BlockingNotAllowedException

问题描述 投票:0回答:1

我正在使用 Quarkus 3.8.6.redhat-00005 开发 Java 应用程序。在我的一门课程中,我尝试在过滤请求时从 ContainerRequestContext 的 InputStream 中读取数据。这是代码:

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 
    }
}

当我尝试运行此程序时,我收到 BlockingNotAllowedException 并显示消息尝试在 io 线程上进行阻塞读取。完整的错误消息是:

org.jboss.resteasy.reactive.common.core.BlockingNotAllowedException:尝试在 io 线程上进行阻塞读取

19:34:45 ERROR [br.co.bb.de.er.re.fi.ExceptionFilter] (vert.x-eventloop-thread-1)  Resp {statusCode=500, body={errors=[{code=ERRO_SISTEMA, message=Erro na execu??o da solicita??o., variaveisMonitoradas={ORIGEM-ERRO=br.com.bb.t99.rest.TesteFilter - linha: 25., MOTIVO-ERRO=org.jboss.resteasy.reactive.common.core.BlockingNotAllowedException: Attempting a blocking read on io thread, ID-REQUISICAO=347f2608-7f8e-447a-8b35-39d08c8a32f5}}]}}: 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 org.jboss.resteasy.reactive.server.vertx.VertxInputStream.readIntoBuffer(VertxInputStream.java:123)
        at org.jboss.resteasy.reactive.server.vertx.VertxInputStream.read(VertxInputStream.java:85)
        at java.base/java.io.InputStream.readNBytes(InputStream.java:412)
        at java.base/java.io.InputStream.readAllBytes(InputStream.java:349)
        at br.com.bb.t99.rest.TesteFilter.filter(TesteFilter.java:25)
        at org.jboss.resteasy.reactive.server.handlers.ResourceRequestFilterHandler.handle(ResourceRequestFilterHandler.java:48)
        at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:131)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
        at org.jboss.resteasy.reactive.server.handlers.RestInitialHandler.beginProcessing(RestInitialHandler.java:48)
        at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:23)
        at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:10)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1285)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:137)
        at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$1.handle(HttpServerCommonHandlers.java:62)
        at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$1.handle(HttpServerCommonHandlers.java:40)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1285)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:137)
        at io.quarkus.resteasy.reactive.server.runtime.ResteasyReactiveRecorder$13.handle(ResteasyReactiveRecorder.java:339)
        at io.quarkus.resteasy.reactive.server.runtime.ResteasyReactiveRecorder$13.handle(ResteasyReactiveRecorder.java:332)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1285)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:137)
        at io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup.handleHotReplacementRequest(VertxHttpHotReplacementSetup.java:133)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:415)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:411)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1285)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:137)
        at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:68)
        at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:37)
        at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$2.handle(HttpServerCommonHandlers.java:86)
        at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$2.handle(HttpServerCommonHandlers.java:69)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:147)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:123)
        at io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup$4.handle(VertxHttpHotReplacementSetup.java:190)
        at io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup$4.handle(VertxHttpHotReplacementSetup.java:181)
        at io.vertx.core.impl.future.FutureImpl$4.onSuccess(FutureImpl.java:176)
        at io.vertx.core.impl.future.FutureBase.lambda$emitSuccess$0(FutureBase.java:60)
        at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173)
        at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:166)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:1583)


19:34:45 ERROR [or.jb.re.re.se.co.RuntimeExceptionMapper] (vert.x-eventloop-thread-1)  A blocking operation occurred on the IO thread. This likely means you need to use the @io.smallrye.common.annotation.Blocking annotation on the Resource method, class or jakarta.ws.rs.core.Application class.

--
2 tests failed (11 passing, 0 skipped), 2 tests were run in 1737078ms. Tests completed at 19:34:18 due to changes to TesteFilter.class.
Press [e] to edit command line args (currently ''), [r] to re-run, [o] Toggle test output, [:] for the terminal, [h] for more options>

我不完全明白为什么会发生这种情况。在 Quarkus 中是否有一种以非阻塞方式读取和编辑请求正文的首选方法?如何解决这个问题?任何指导将不胜感激。

我已经尝试将 @io.smallrye.common.annotation.Blocking 注释添加到端点、扩展 jakarta.ws.rs.core.Application 的类以及过滤器本身。然而,所有这些尝试都未能成功解决问题。

java quarkus quarkus-reactive
1个回答
0
投票

您使用的方法会导致此错误,因为在使用 @PreMatching 过滤器时,预计操作是非阻塞的,因为 事件循环始终用于这些操作在ContainerRequestContext中读取InputStream的操作是阻塞的,由于它是在事件循环中执行的,所以会抛出org.jboss.resteasy.reactive.common.core.BlockingNotAllowedException。

考虑到抛出的异常以及您在之前的评论中提到的过滤器的目的,我相信该问题的可能解决方案如下:

  1. 从类中删除@PreMatching注释。
  2. 直接在方法上使用@ServerRequestFilter注解(不传递属性,使用默认配置)。这将确保该方法在请求到达端点之前被调用,并且其操作将以非阻塞方式执行(在工作线程中执行)。
  3. 删除ContainerRequestFilter接口的实现。这很重要,因为在实现此接口的类中使用 @ServerRequestFilter 会导致该方法在到达端点之前执行两次。
package com.example;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.logging.Logger;

import org.jboss.resteasy.reactive.server.ServerRequestFilter;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.vertx.core.http.HttpServerRequest;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.PreMatching;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.ext.Provider;

@Provider
public class TesteFilter {
    private static final Logger LOGGER = Logger.getLogger(TesteFilter.class.getName());

    @Context
    HttpServerRequest request;

    @ServerRequestFilter
    public void doFilter(ContainerRequestContext containerRequestContext) throws IOException {
        LOGGER.info("Intercepting the request in the Filter");

        String body = new String(containerRequestContext.getEntityStream().readAllBytes());
        LOGGER.info("Original request body: " + body);

        ObjectMapper mapper = new ObjectMapper();
        Language language = mapper.readValue(body, Language.class);
        
        String modifiedBody = mapper.writeValueAsString(
                new Language("Modified-" + language.getType(),
                             "Modified-" + language.getName()
                            ));
        LOGGER.info("Modified request body: " + modifiedBody);

        containerRequestContext.setEntityStream(new ByteArrayInputStream(modifiedBody.getBytes()));
    }
}

以下是涵盖该主题的 Quarkus 文档: 请求或响应过滤器 - Quarkus 文档

最后但同样重要的是,您提到您正在使用 Quarkus 版本 3.8.6.redhat-00005,这让我相信您已经订阅了 Quarkus 的 Red Hat Build。 不要犹豫,向红帽开具支持票,以获得有关此类问题的帮助。

© www.soinside.com 2019 - 2024. All rights reserved.