追查一些例子应该不会太难。我在WASdev/sample.javaee7.servlet.nonblocking 找到了 IBM 的一个。在 Spring 或 Spring Boot 中使用 javax.servlet API 只需要求 Spring 注入 HttpServletRequest 或 HttpServletResponse。所以,一个简单的例子可能是:
@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();
}
});
}
}
这只是创建一个WriteListener 并将其附加到请求输出流然后返回。没什么特别的。
编辑:关键是 servlet 容器,例如 Tomcat,当数据可以被写入而不阻塞时调用onWritePossible。更多信息请访问Non-blocking I/O using Servlet 3.1: Scalable applications using Java EE 7 (TOTD #188).。
侦听器(和编写器)具有回调方法,当内容可供读取或可以在不阻塞的情况下写入时调用。
因此,onWritePossible 仅在 out.println 可以在没有阻塞的情况下被调用时才被调用。
调用 setXXXListener 方法表示使用非阻塞 I/O 而不是传统的 I/O。
大概你必须做什么检查output.isReady 以了解你是否可以继续写入字节。似乎您必须与发送方/接收方就块大小达成某种隐含协议。我从未使用过它,所以我不知道,但是您要求在 Spring 框架中提供一个示例,这就是所提供的。
因此,只有在 out.println 可以不阻塞地调用时才调用 onWritePossible。 听起来是正确的,但我如何理解可以写入多少字节?我应该如何控制?
编辑 2:这是一个很好的问题,我无法给你一个确切的答案。我假设当服务器在主 servlet 的单独(异步)线程中执行代码时调用 onWritePossible。从您检查input.isReady() 或output.isReady() 的示例中,我假设这会阻塞您的线程,直到发送方/接收方准备好更多。由于这是异步完成的,因此服务器本身不会被阻塞并且可以处理其他请求。我从来没有用过这个,所以我不是专家。
当我说发送方/接收方会就块大小达成某种隐含协议时,这意味着如果接收方能够接受 1024 字节块,那么当output.isReady 为真时,您将写入该数量。您必须通过阅读文档来了解这一点,而 api 中没有关于它的任何内容。否则,您可以写入单个字节,但 oracle 的示例使用 1024 字节块。 1024 字节块是流式 I/O 的相当标准的块大小。上面的示例必须扩展为在 while 循环中写入字节,就像在 oracle 示例中所示。这是留给读者的练习。
Project reactor 和 Spring Webflux 有 backpressure 的概念,可以更仔细地解决这个问题。那将是一个单独的问题,我还没有仔细研究过如何将发送者和接收者结合起来(反之亦然)。