Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent Jersey Server keeping thread alive after client has closed connection

I am working on a small application that requires streaming a varied length output from a REST endpoint to a client using Jersey 2.0 (version 2.19). Although less that an ideal due to the existing framework of the client application, the client may cancel the request at any time.

I can successfully return a streaming output using StreamingOutput as seen with my code below.

@GET
public Response test() {

        StreamingOutput stream = new StreamingOutput() {
            @Override
            public void write(OutputStream os) throws IOException, WebApplicationException {

                Writer writer = new BufferedWriter(new OutputStreamWriter(os));

                for (int i = 0; i < 500000; i++) {
                    LOGGER.info(Integer.toString(i));
                    writer.write(String.valueOf(i) + "\n");
                    writer.flush();
                }

                writer.close();
            }
        };

        return Response.ok(stream).build();
}

When I call the endpoint from a client, and let it finish (so return numbers from 0 - 499999) I do not suffer any issues and no threads are held in Tomcat memory (as seen in Tomcat manager).
However, if the client cancels the request before it has completed, the process no longer continues (as seen by logging i to a log file) but according to Tomcat manager it is still holding onto a thread, with the thread time increasing but the bytes sent staying the same, these stick around and do not time out after an amount of time. There are no Exceptions thrown and logged in either the application log or Tomcat logs, but on stopping Tomcat, you can then see references to these threads in the tomcat logs saying it's likely to cause a memory leak:

SEVERE: The web application [/streaming-1.0-SNAPSHOT] created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@27d415d9]) and a value of type [org.glassfish.jersey.process.internal.RequestScope.Instance] (value [Instance{id=3ad9c61c-22cd-40d5-b810-59bff3feafa9, referenceCounter=2, store size=4}]) but failed to remove it when the web application was stopped. This is very likely to create a memory leak.

So the question is, is there a way to prevent these threads remaining once a client has cancelled a connection to the server?

I am making a giant presumption this is Jersey rather than Tomcat, partially wishful thinking as I may not necessarily be able to get any adjustments to the production version of tomcat made.

like image 809
Crazy Dino Avatar asked Oct 30 '22 23:10

Crazy Dino


1 Answers

Have you tried creating the BufferedWriter a try-with-resources statement or catching the IOException?

When the client terminates the connection early, on the server, writer.write throws an IOException which is not caught, so the BufferedWriter is never closed, and the underlying Tomcat-provided OutputStream is never closed.

try (Writer writer = new BufferedWriter(new OutputStreamWriter(os))) {
  ...
} catch(IOException ioe) {
  // log client termination.
}
like image 183
space Avatar answered Nov 15 '22 06:11

space