Undertow如何实现非阻塞IO?

13

我正在使用Undertow创建一个简单的应用程序。

public class App {
    public static void main(String[] args) {
        Undertow server = Undertow.builder().addListener(8080, "localhost")
                .setHandler(new HttpHandler() {

                    public void handleRequest(HttpServerExchange exchange) throws Exception {
                        Thread.sleep(5000);
                        exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                        exchange.getResponseSender().send("Hello World");
                    }

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

我在浏览器标签页上打开了localhost:8080,然后我又打开了第二个标签页,同样是在localhost:8080上。

这一次,第一个标签页将等待5秒钟,而第二个标签页将等待10秒钟。

为什么会这样呢?

3个回答

15

HttpHandler运行在I/O线程中。正如文档所述:

IO线程执行非阻塞任务,永远不应执行阻塞操作,因为它们负责多个连接,当操作被阻塞时,其他连接将会挂起。每个CPU核心一个IO线程是合理的默认值。

请求生命周期文档讨论了如何将请求分派到工作线程中:

import io.undertow.Undertow;
import io.undertow.server.*;
import io.undertow.util.Headers;

public class Under {
  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();
  }
}

我注意到并不是每个请求都会有一个工作线程 - 当我在header put上设置断点时,每个客户端大约会得到一个线程。在Undertow和底层的XNIO文档中都存在一些空白,所以我不确定意图是什么。


@McDowell Node.js在异步通信方面有何不同? - johnny

9
Undertow使用NIO,这意味着单个线程处理所有请求。如果您希望在请求处理程序中执行阻塞操作,则必须将此操作分派到工作线程中。
在您的示例中,您使线程休眠,这意味着所有请求处理都会被暂停,因为该线程处理所有请求。
然而,即使您将操作分派到工作线程并使其休眠,您仍将看到您提到的阻塞问题。这是因为您在同一浏览器上的多个选项卡中打开了相同的URL。浏览器有自己的内部阻塞。如果您在不同的选项卡中打开相同的URL,则第二个URL将在第一个完成后开始请求。尝试任何您想要查看的URL以便自行了解。您可能会因此浏览器行为感到困惑。

8
最简单的方法是将您的处理程序包装在一个BlockingHandler中。
import io.undertow.Undertow;
import io.undertow.server.*;
import io.undertow.server.handlers.BlockingHandler;
import io.undertow.util.Headers;

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接