追查一些例子应该不难。我在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();
}
});
}
}
这只是创建 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
,可以更仔细地解决这个问题。那将是一个单独的问题,我还没有仔细研究过如何将发送者和接收者结合起来(反之亦然)。