Search code examples
javamultithreadinghttpasynchronousundertow

Undertown. Async responce with another thread


I try to realize how to build an true-async http server with Undertow. How to send a response asynchronously if I have another thread which one is processing a request already?
I wrote code like this:

    Undertow server = Undertow.builder()
            .addHttpListener(8080, "localhost")
            .setHandler(exchange -> {
                CompletableFuture.runAsync(() -> {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }).thenAccept(string -> {
                    exchange.getResponseHeaders()
                            .put(Headers.CONTENT_TYPE, "text/plain");
                    exchange.getResponseSender().send("Hello World");
                    exchange.endExchange();
                }).exceptionally(throwable -> {
                    System.out.println(throwable.toString());
                    return null;
                });
            }).build();
    server.start();

but this server response 200 with no data and in logs

java.lang.IllegalStateException: UT000127: Response has already been sent

When I use io.undertow.server.HttpServerExchange#dispatch(java.lang.Runnable) method like this:

    Undertow server = Undertow.builder()
            .addHttpListener(8080, "localhost")
            .setHandler(exchange -> {

                exchange.dispatch(() -> {

                    CompletableFuture.runAsync(() -> {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }).thenAccept(string -> {
                        exchange.getResponseHeaders()
                                .put(Headers.CONTENT_TYPE,"text/plain");
                        exchange.getResponseSender().send("Hello World");
                        exchange.endExchange();
                    }).exceptionally(throwable -> {
                        System.out.println(throwable.toString());
                        return null;
                    });

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

of course a response "Hello World" as expected but server creates new thread for every request!

(jvisualvm after 10 parallel requests) jvisualvm after 10 parallel requests


Solution

  • If you call dispatch() without passing an executor, like this:

    exchange.dispatch(() -> {
        //...
    });
    

    it will dispatch it to the XNIO worker thread pool. It will not necessarily "create new thread for every request".

    If you want to not dispatch it to another thread, you should do this:

    exchange.dispatch(SameThreadExecutor.INSTANCE, () -> {
        //...
    });