追寻一些例子不应该太难。我在IBM找到了一个WASdev/sample.javaee7.servlet.nonblocking的例子。在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
。更多信息请参见使用Servlet 3.1进行非阻塞I/O:使用Java EE 7构建可扩展应用程序(TOTD#188)。。
侦听器(和编写器)具有回调方法,在内容可供读取或可以无阻塞地写入时调用该方法。
因此,只有在可以无阻塞地调用out.println
时才会调用onWritePossible
。
调用setXXXListener方法表示使用非阻塞I/O而不是传统I/O。
大概你需要检查 output.isReady
是否就绪,才能继续写入字节。看起来你需要与发送/接收方就块大小达成某种隐式协议。我从未使用过这个,所以不清楚,但你要求在 Spring 框架中提供一个示例,这就是提供的。
因此,只有在 out.println 可以无阻塞调用时才会调用 onWritePossible。 听起来没问题,但我该如何理解可以写入多少字节?我该如何控制这个?
编辑 2:这是一个非常好的问题,我不能给你一个确切的答案。我认为当服务器在单独的(异步)线程中执行代码时,会调用 onWritePossible
。从示例中,你检查 input.isReady()
或者 output.isReady()
,我想这会阻塞你的线程,直到发送/接收方准备好获取更多信息。由于这是异步完成的,因此服务器本身不会被阻塞,并且可以处理其他请求。我从未使用过这个,所以我不是专家。
当我说发送方/接收方会有某种隐含协议关于块大小,这意味着如果接收方能够接受1024字节的块,那么在output.isReady
为true时,您将写入该数量。您必须通过阅读文档来了解这一点,API中没有关于它的信息。否则,您可以编写单个字节,但是Oracle的示例使用1024字节块。 1024字节块是流I / O的相当标准的块大小。上面的示例必须扩展为像Oracle示例中显示的while
循环中写入字节。这是留给读者的练习。
Project Reactor和Spring Webflux具有可能更加小心地处理此问题的backpressure
概念。那将是一个单独的问题,我还没有深入研究如何将发送方和接收方(或反之亦然)联系起来。