[英]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.