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

Posted

技术标签:

【中文标题】如何在 Undertow 的非阻塞处理程序中执行阻塞代码?【英文标题】:How to execute blocking code in a non-blocking Handler in Undertow? 【发布时间】:2019-11-16 03:15:19 【问题描述】:

如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() 

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

但是,如果某些代码大部分时间是非阻塞的,但有时需要阻塞调用,如何将那个阻塞调用变成非阻塞调用?例如,如果请求的值存在于缓存中,则以下代码不会阻塞(它只是从某个 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,除非真的需要使用输入流,否则这个调用还是阻塞的。

调用此方法会将交换置于阻塞模式,并创建一个 BlockingHttpExchange 对象来存储流。当交换是 在阻塞模式下,输入流方法变得可用,除了 目前阻止一个 非阻塞模式

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

【问题讨论】:

【参考方案1】:

正确的方法是在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();
  

【讨论】:

以上是关于如何在 Undertow 的非阻塞处理程序中执行阻塞代码?的主要内容,如果未能解决你的问题,请参考以下文章

同步异步阻塞非阻塞

开微服务项目tomcat更换成undertow

阻塞和非阻塞子进程调用

SpringBoot2使用Undertow来提高应用性能(spring-boot-starter-undertow)

Spring Boot 内嵌容器Undertow参数设置

Spring Boot 内嵌容器Undertow参数设置