18

有 2 种不同的功能可用:

  1. servlet 3.0允许在与容器线程不同的线程中处理请求。

  2. servlet 3.1允许在不阻塞读/写线程的情况下读/写套接字

网上有很多关于 servlet 3.0 特性的例子。我们可以很容易地在 Spring 中使用它。我们只需要返回DefferedResultCompletableFuture

但是我在春天找不到使用servlet 3.1的例子。据我所知,我们必须注册WriteListenerReadListener在里面做圆顶脏活。但我找不到那个听众的例子。我相信这并不容易。

您能否提供 Spring 中 servlet 3.1 功能的示例以及 Listener 实现的解释?

4

3 回答 3

1

对于 servlet 3.1,您可以通过使用 Reactive Streams 桥来支持非阻塞 I/O

Servlet 3.1+ 容器

要作为 WAR 部署到任何 Servlet 3.1+ 容器,您可以在 WAR 中扩展并包含 {api-spring-framework}/web/server/adapter/AbstractReactiveWebInitializer.html[AbstractReactiveWebInitializer]。该类使用 ServletHttpHandlerAdapter 包装一个 HttpHandler 并将其注册为 Servlet。

所以你应该扩展添加异步支持的AbstractReactiveWebInitializer

registration.setAsyncSupported(true);

以及ServletHttpHandlerAdapter中的支持

AsyncContext asyncContext = request.startAsync();
于 2019-07-18T14:23:46.143 回答
0

如果您正在寻找 Spring/Servlet 3.1 非阻塞 HTTP API 声明的示例,请尝试以下操作:

@GetMapping(value = "/asyncNonBlockingRequestProcessing")
public CompletableFuture<String> asyncNonBlockingRequestProcessing(){
        ListenableFuture<String> listenableFuture = getRequest.execute(new AsyncCompletionHandler<String>() {
            @Override
            public String onCompleted(Response response) throws Exception {
                logger.debug("Async Non Blocking Request processing completed");
                return "Async Non blocking...";
             }
        });
        return listenableFuture.toCompletableFuture();
}

需要 Servlet 容器级别的 Spring Web 5.0+ 和 Servlet 3.1 支持(Tomcat 8.5+、Jetty 9.4+、WildFly 10+)

于 2019-07-04T19:06:59.510 回答
0

追查一些例子应该不难。我在WASdev/sample.javaee7.servlet.nonblocking从 IBM 找到了一个。在 Spring 或 Spring Boot 中使用javax.servletAPI 只是要求 Spring 注入HttpServletRequestHttpServletResponse. 所以,一个简单的例子可能是:

@SpringBootApplication
@Controller
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @RequestMapping(path = "")
    public void writeStream(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ServletOutputStream output = response.getOutputStream();
        AsyncContext context = request.startAsync();
        output.setWriteListener(new WriteListener() {
            @Override
            public void onWritePossible() throws IOException {
                if ( output.isReady() ) {
                    output.println("WriteListener:onWritePossible() called to send response data on thread : " + Thread.currentThread().getName());
                }
                context.complete();
            }
            @Override
            public void onError(Throwable t) {
                context.complete();
            }
        });
    }
}

这只是创建 aWriteListener并将其附加到请求输出流然后返回。没有什么花哨。

编辑:关键是servlet 容器(例如Tomcat)onWritePossible在可以写入数据时调用而不会阻塞。更多信息请参阅使用 Servlet 3.1 的非阻塞 I/O:使用 Java EE 7 的可扩展应用程序(TOTD #188)。.

侦听器(和编写器)具有回调方法,当内容可供读取或可以在不阻塞的情况下写入时调用这些方法。

因此,只有在可以不阻塞地调用onWritePossible时才调用。out.println

调用 setXXXListener 方法表示使用非阻塞 I/O 而不是传统的 I/O。

大概你必须做什么检查output.isReady你是否可以继续写字节。似乎您必须与发送方/接收方就块大小达成某种隐含协议。我从未使用过它,所以我不知道,但是您要求在 Spring 框架中提供一个示例,这就是所提供的。

因此,只有在可以无阻塞地调用 out.println 时才调用 onWritePossible。 这听起来是正确的,但我怎么能理解可以写入多少字节?我应该如何控制这个?

编辑2:这是一个很好的问题,我不能给你一个确切的答案。我假设onWritePossible当服务器在主 servlet 的单独(异步)线程中执行代码时调用它。从您检查的示例中,input.isReady()或者output.isReady()我假设这会阻止您的线程,直到发送者/接收者准备好接受更多。由于这是异步完成的,因此服务器本身不会被阻塞并且可以处理其他请求。我从来没有用过这个,所以我不是专家。

当我说发送方/接收方会就块大小达成某种隐含协议时,这意味着如果接收方能够接受 1024 字节的块,那么当output.isReady为真时,您将写入该数量。您必须通过阅读文档来了解这一点,而 api 中没有关于它的任何内容。否则,您可以写入单个字节,但 oracle 的示例使用 1024 字节块。1024 字节块是流式 I/O 的相当标准的块大小。上面的示例必须扩展为在while循环中写入字节,如 oracle 示例中所示。这是留给读者的练习。

Project reactor 和 Spring Webflux 有这样的概念backpressure,可以更仔细地解决这个问题。那将是一个单独的问题,我还没有仔细研究过如何将发送者和接收者结合起来(反之亦然)。

于 2019-07-09T16:58:55.950 回答