簡體   English   中英

如何處理 ContainerRequestFilter 中的阻塞操作 Quarkus/Vert.x

[英]How should Blocking Operations in a ContainerRequestFilter be handled Quarkus/Vert.x

背景

我們正在實現服務之間通信的簽名請求機制。 該過程的一部分會生成關於請求正文內容的摘要。 為了在接收時驗證正文,我們在接收器處重新生成摘要並進行比較。 這是非常直接的東西。

@PreMatching
@Priority(Priorities.ENTITY_CODER)
public class DigestValidationFilter implements ContainerRequestFilter {

   private final DigestGenerator generator;

   @Inject
   public DigestValidationFilter(DigestGenerator generator) {
      this.generator = generator;
   }

   @Override
   public void filter(ContainerRequestContext context) throws IOException {
      if (context.hasEntity() && context.getHeaderString(Headers.DIGEST) != null) {
         String digest = context.getHeaderString(Headers.DIGEST);

         ByteArrayOutputStream body = new ByteArrayOutputStream();
         try (InputStream stream = context.getEntityStream()) {
             stream.transferTo(body); // <-- This is line 36 from the provided stack-trace
         }

         String algorithm = digest.split("=", 2)[0];
         try {
             String calculated = generator.generate(algorithm, body.toByteArray());

             if (digest.equals(calculated)) {
                 context.setEntityStream(new ByteArrayInputStream(body.toByteArray()));
             } else {
                 throw new InvalidDigestException("Calculated digest does not match supplied digest. Request body may have been tampered with.");
             }
         } catch (NoSuchAlgorithmException e) {
             throw new InvalidDigestException(String.format("Unsupported hash algorithm: %s", algorithm), e);
         }
      }
   }
}

上述過濾器作為 java-lib 提供給服務。 我們還提供了一組 RequestFilters,可用於各種 Http 客戶端,即 okhttp3、apache-httpclient 等。這些客戶端僅在正文“可重復”時生成摘要,即不流式傳輸。

問題

在 Jersey 服務和 Spring 引導服務中,我們沒有遇到問題。 但是,當我們使用 Quarkus 時,我們會收到以下堆棧跟蹤:

2022-09-02 15:18:25 5.13.0 ERROR 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 javax.ws.rs.core.Application class. 
2022-09-02 15:18:25 5.13.0 ERROR HTTP Request to /v1/policy/internal/policies/72575947-45ac-4358-bc40-b5c7ffbd3f35/target-resources failed, error id: c79aa557-c742-43d7-93d9-0e362b2dff79-1 
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:242)
    at org.jboss.resteasy.reactive.server.vertx.VertxInputStream.readIntoBuffer(VertxInputStream.java:120)
    at org.jboss.resteasy.reactive.server.vertx.VertxInputStream.read(VertxInputStream.java:82)
    at java.base/java.io.InputStream.transferTo(InputStream.java:782)
    at com.###.ciam.jaxrs.DigestValidationFilter.filter(DigestValidationFilter.java:36)
    at org.jboss.resteasy.reactive.server.handlers.ResourceRequestFilterHandler.handle(ResourceRequestFilterHandler.java:47)
    at org.jboss.resteasy.reactive.server.handlers.ResourceRequestFilterHandler.handle(ResourceRequestFilterHandler.java:8)
    at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:141)
    at org.jboss.resteasy.reactive.server.handlers.RestInitialHandler.beginProcessing(RestInitialHandler.java:49)
    at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:17)
    at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:7)
    at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
    at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
    at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
    at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:67) ... elided ...

我完全理解為什么 Vert.x 想要阻止請求處理線程上長時間運行的 I/O 操作。 也就是說,異常中提供的建議僅考慮請求處理結束時的 I/O 操作,即它假設 I/O 發生在端點中。 盡管我們確實控制了過濾器代碼,但它位於外部庫中,使其幾乎就像一個第三方庫。

我的問題

處理這個問題的正確方法是什么?

我一直在搜索文檔,但還沒有偶然發現答案(或者沒有認識到答案)。 我應該查看一組推薦的文檔嗎?

https://quarkus.io/guides/resteasy-reactive#request-or-response-filters

https://smallrye.io/smallrye-mutiny/1.7.0/guides/framework-integration/

    @RequestScoped
    class Filter(
        private val vertx: Vertx
    ) {
    
    //  you can run blocking code on mutiny's Infrastructure defaultWorkerPool
        @ServerRequestFilter
        fun filter(requestContext: ContainerRequestContext): Uni<RestResponse<*>> {
            return Uni.createFrom().item { work() }
                .map<RestResponse<*>> { null }
                .runSubscriptionOn(Infrastructure.getDefaultWorkerPool())
        }
    // or use vertx.executeBlocking api
        @ServerRequestFilter
        fun filter(requestContext: ContainerRequestContext): Uni<RestResponse<*>> {
            return vertx.executeBlocking(
                Uni.createFrom().item { work() }
                    .map { null }
            )
        }
    
    
    
        private fun work(){
            Log.info("filter")
            Thread.sleep(3000)
        }
    }

最后,異常中的建議讓我簡單地注釋了一個委托 ContainerRequestFilter:

public class DigestValidationFilterBlocking implements ContainerRequestFilter {

    private final DigestValidationFilter delegate;

    public DigestValidationFilterBlocking(DigestValidationFilter delegate) {
        this.delegate = delegate;
    }

    @Blocking // <-- This annotation allowed Vert.x to accept the I/O operation
    @Override
    public void filter(ContainerRequestContext context) throws IOException {
        delegate.filter(context);
    }
}

我有同樣的問題。 您可以嘗試在@ServerRequestFilter中使用它:

@Context
HttpServerRequest request;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM