New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HTTP persistent connections for HTTP Invoker and RestTemplate [SPR-14040] #18612
Comments
Benjamin Bargeton commented When I searched for any similar ticket prior to opening that case I've felt on #12775 After double checking I think that calling connection.disconnect() in SimpleClientHttpResponse is wrong and that the original reporter of the ticket was right. Calling disconnect for every RestTemplate call, will not only prevent the http persistent connections but it can even lead to closing two connections at once as stated in the source comments of HttpUrlConnection (see below). Calling close on the inputstream as described in the original ticket is better because it already guarantees that the connection will be closed at some point:
If you agree, could you reopen that ticket, or do you want me to create another one? /*
* If we have an input stream this means we received a response
* from the server. That stream may have been read to EOF and
* dependening on the stream type may already be closed or the
* the http client may be returned to the keep-alive cache.
* If the http client has been returned to the keep-alive cache
* it may be closed (idle timeout) or may be allocated to
* another request.
*
* In other to avoid timing issues we close the input stream
* which will either close the underlying connection or return
* the client to the cache. If there's a possibility that the
* client has been returned to the cache (ie: stream is a keep
* alive stream or a chunked input stream) then we remove an
* idle connection to the server. Note that this approach
* can be considered an approximation in that we may close a
* different idle connection to that used by the request.
* Additionally it's possible that we close two connections
* - the first becuase it wasn't an EOF (and couldn't be
* hurried) - the second, another idle connection to the
* same server. The is okay because "disconnect" is an
* indication that the application doesn't intend to access
* this http server for a while.
*/ http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java |
Juergen Hoeller commented Let's repurpose this one for a broader context... |
Brian Clozel commented I've made progress on the Now I've got a few questions regarding
When looking at the proposed solution (i.e. not flushing the response outputstream on the server side), it seems that we're losing the guarantee of our data being actually written to the socket if they're buffered by the outputstream implementation. In all cases, the outputstream will be closed (see Here are my questions:
Thanks for this (very) detailed report, this gives us the chance to revisit things we previously missed. |
Benjamin Bargeton commented Hi Brian, For step 3 I don't know if it's what you wanted to say but the flush doesn't add the chunked encoding trailer in itself. About the close in To try to answer your questions:
So basically every time flush is call on the
Also from that comment it seems that extra headers could be sent after after the last data chunk and before the closing trailer (never had the case though). Anyway on my tests, fixing it on the server side was good enough (I didn't see any close due to a not received trailer) and we didn't had a case of lost data since it was delivered. Thanks for your last comment, I've learn so much by reading/using Spring projects that it's a pleasure to bring at last a contribution (even if it's a very small one:-)). |
Brian Clozel commented Thanks Benjamin Bargeton! I was indeed mixing both issues which are quite different but in the end have a strong impact on HTTP performance. Again, thanks for this very detailed report! |
Rossen Stoyanchev commented Should the resolution of #12775 be updated as duplicate or superseded by the fix for this ticket? |
Benjamin Bargeton opened SPR-14040 and commented
Normally HttpURLConnection is maintaining persistent connections under the hood.
One condition to reuse a connection is that the http response must be fully consumed (https://docs.oracle.com/javase/1.5.0/docs/guide/net/http-keepalive.html)
The combination of using an ObjectOutputStream over an Http response output stream as done in HttpInvokerServiceExporter make this condition hard to be satisfied.
The oos.close() method called in writeRemoteInvocationResult(...) is doing the following things (i've inlined a bit the code):
Forcing the flush on the servlet reponse output stream before closing it has 2 side effects:
They are many way of fixing this issue.
The one I've picked for the moment is to enclose the response stream in a wrapper that do nothing on flush (using decorateOutputStream()) but I think this should be the default in HttpInvokerServiceExporter because this can lead to serious performance issue under load
Note: I believe this is the same issue as the one described in #12645
Affects: 4.2.5
Attachments:
Issue Links:
Referenced from: commits b947bfe, eec22f5
The text was updated successfully, but these errors were encountered: