如何在 Undertow 的非阻塞处理程序中执行阻塞代码?

How to execute blocking code in a non-blocking Handler in Undertow?

separate question中所述,使用Undertow时,所有的处理都应该在专用的Worker线程池中完成,如下所示:

public class Start {

  public static void main(String[] args) {
    Undertow server = Undertow.builder()
        .addListener(8080, "localhost")
        .setHandler(new HttpHandler() {
          public void handleRequest(HttpServerExchange exchange)
              throws Exception {
            if (exchange.isInIoThread()) {
              exchange.dispatch(this);
              return;
            }
            exchange.getResponseHeaders()
                    .put(Headers.CONTENT_TYPE, "text/plain");
            exchange.getResponseSender()
                    .send("Hello World");
          }
        })
        .build();
    server.start();
  }
}

我知道 BlockingHandler 可用于明确告诉 Undertow 将请求安排在专用线程池上以阻止请求。我们可以通过将 HttpHandler 包装在 BlockingHandler 的实例中来调整上面的示例,如下所示:

        .setHandler(new BlockingHandler(new HttpHandler() {

这适用于我们知道总是阻塞的调用。

然而,如果某些代码大部分时间是非阻塞的,但有时需要阻塞调用,如何将 that 阻塞调用变为非阻塞调用?例如,如果请求的值存在于缓存中,则以下代码不会阻塞(它只是从某些 Map<> 中获取),但如果不存在,则必须从数据库中获取。

public class Start {

  public static void main(String[] args) {
    Undertow server = Undertow.builder()
        .addListener(8080, "localhost")
        .setHandler(new HttpHandler() {
          public void handleRequest(HttpServerExchange exchange)
              throws Exception {
            if (exchange.isInIoThread()) {
              exchange.dispatch(this);
              return;
            }
            if (valueIsPresentInCache(exchange)) {
              return valueFromCache;  // non-blocking
            } else {
              return fetchValueFromDatabase(); // blocking!!!
            }
          }
        })
        .build();
    server.start();
  }
}

根据docs,有一个方法HttpServerExchange.startBlocking(),但是根据JavaDoc,除非你真的需要使用输入流,否则这个调用仍然是一个阻塞方法。

Calling this method puts the exchange in blocking mode, and creates a BlockingHttpExchange object to store the streams. When an exchange is in blocking mode the input stream methods become available, other than that there is presently no major difference between blocking an non-blocking modes

如何将这个阻塞调用变成非阻塞调用?

正确的做法是在IO线程中实际执行逻辑,如果它是非阻塞的话。否则,将请求委托给专用线程,如下所示:

public class Example {

  public static void main(String[] args) {
    Undertow server = Undertow.builder()
        .addListener(8080, "localhost")
        .setHandler(new HttpHandler() {
          public void handleRequest(HttpServerExchange exchange)
              throws Exception {

            if (valueIsPresentInCache(exchange)) {
              getValueFromCache();  // non-blocking, can be done from IO thread           
            } else {

              if (exchange.isInIoThread()) {
                exchange.dispatch(this);
                // we return immediately, otherwise this request will be
                // handled both in IO thread and a Worker thread, throwing
                // an exception
                return;
              }

              fetchValueFromDatabase(); // blocking!!!

            }
          }
        })
        .build();
    server.start();
  }
}