Jersey streaming 2?

I am trying to get json running in jersey 2. For the life of me, nothing flows until the thread is complete.

I've tried this example trying to simulate a slow data producer.

@Path("/foo")
@GET
public void getAsyncStream(@Suspended AsyncResponse response) {
    StreamingOutput streamingOutput = output -> {

        JsonGenerator jg = new ObjectMapper().getFactory().createGenerator(output, JsonEncoding.UTF8);
        jg.writeStartArray();

        for (int i = 0; i < 100; i++) {
            jg.writeObject(i);

            try {
                Thread.sleep(100);
            }
            catch (InterruptedException e) {
                logger.error(e, "Error");
            }
        }

        jg.writeEndArray();

        jg.flush();
        jg.close();

    };

    response.resume(Response.ok(streamingOutput).build());
}

      

And yet the jersey just sits there until the json generator is done to return the results. I am watching the results go through charles proxy.

Do I need to activate something? Not sure why this won't happen


Edit:

It might actually work, just not the way I expected. I don't think the thread is writing things in real time, which is what I wanted, all the more so as not to buffer the responses and write them out to the client immediately. If I run a loop of a million and a non-sleeping thread, then the data will be written in chunks without loading it into memory.

+3


source to share


1 answer


Edit it correctly. It works as expected. StreamingOutput

is just a wrapper that allows us to write directly to the response stream, but doesn't really mean that the response is sent to each side of the server by writing to the stream.
Also AsyncResponse

does not provide any other answer as far as the client is concerned. It just helps to increase the throughput when doing long tasks. The long running task must run on a different thread, so the method can return.

You're looking for Chunked Output instead

Jersey offers a facility for sending a response to the client in several more or less independent chunks using dedicated output. Each piece of response usually takes some (longer) time to prepare before sending it to the client. The most important fact about response chunks is that you want to send them immediately to the client when they become available, without waiting for the rest of the chunks to become available.

Not sure how it will work for your specific use case as it JsonGenerator

expects OutputStream

(from which ChuckedOutput

we are not using), but here is a simpler example

@Path("async")
public class AsyncResource {

    @GET
    public ChunkedOutput<String> getChunkedStream() throws Exception {
        final ChunkedOutput<String> output = new ChunkedOutput<>(String.class);

        new Thread(() -> {
            try {
                String chunk = "Message";

                for (int i = 0; i < 10; i++) {
                    output.write(chunk + "#" + i);
                    Thread.sleep(1000);
                }
            } catch (Exception e) {
            } finally {
                try {
                    output.close();
                } catch (IOException ex) {
                    Logger.getLogger(AsyncResource.class.getName())
                          .log(Level.SEVERE, null, ex);
                }
            }
        }).start();
        return output;
    }
}

      



Note. I had a problem getting this to work at first. I would only get a deferred full result. The problem seemed to be something completely separate from the program. This was my AVG causing the problem. Some function called "LinkScanner" stopped this chunking process. I disabled this feature and it started working.

I didn't understand much and I'm not sure what the security implications are, so I'm not sure why the AVG app has problems with it.

enter image description here


EDIT

The real issue seems to be because Jersey is buffering the response to calculate the Content-Length header. You can see this post how you can change this behavior

+4


source







All Articles