Shouldn't be too hard to chase down some examples. I found one from IBM at WASdev/sample.javaee7.servlet.nonblocking . Working with javax.servlet
API in Spring or Spring Boot is just a matter of asking Spring to inject HttpServletRequest
or HttpServletResponse
. So, a simple example could be:
@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();
}
});
}
}
This simply creates a WriteListener
and attaches it to the request output stream then returns. Nothing fancy.
EDITS: The point is that the servlet container, e.g., Tomcat, calls onWritePossible
when data can be written without blocking. More on this at Non-blocking I/O using Servlet 3.1: Scalable applications using Java EE 7 (TOTD #188)..
The listeners (and writers) have callback methods that are invoked when the content is available to be read or can be written without blocking.
So therefore onWritePossible
is only called when out.println
can be called without blocking.
Invoking setXXXListener methods indicate that non-blocking I/O is used instead of the traditional I/O.
Presumably what you have to do check output.isReady
to know if you can continue to write bytes. It seems to be that you would have to have some sort of implicit agreement with the sender/receiver about block sizes. I have never used it so I don't know, but you asked for an example of this in Spring framework and that's what is provided.
So therefore onWritePossible is only called when out.println can be called without blocking. It is sounds correct but how can I understand how many bytes can be written ? How should I control this?
EDIT 2: That is a very good question that I can't give you an exact answer to. I would assume that onWritePossible
is called when the server executes the code in a separate (asynchronous) thread from the main servlet. From the example you check input.isReady()
or output.isReady()
and I assume that blocks your thread until the sender/receiver is ready for more. Since this is done asynchronously the server itself is not blocked and can handle other requests. I have never used this so I am not an expert.
When I said there would be some sort of implicit agreement with the sender/receiver about block sizes that means that if the receiver is capable of accepting 1024 byte blocks then you would write that amount when output.isReady
is true. You would have to have knowledge about that by reading the documentation, nothing in the api about it. Otherwise you could write single bytes but the example from oracle uses 1024 byte blocks. 1024 byte blocks is a fairly standard block size for streaming I/O. The example above would have to be expanded to write bytes in a while
loop like it is shown in the oracle example. That is an exercise left for the reader.
Project reactor and Spring Webflux have the concept of backpressure
that may address this more carefully. That would be a separate question and I have not looked closely into how that couples senders and receivers (or vice versa).