>>> Timothy Owen Reilly <[EMAIL PROTECTED]> 16-Jun-00
2:54:25 PM >>>
"OutputStream Expo" continues - booth 44 "closing connections"
(ie: this is another long explanation)
>We're using a servlet to log web accesses. Basically our
>servlet is called via an image tag in the following sequence:
>1) The arguments passed to the servlet are pulled out of the
request
> object.
>2) The ServletOutputStream is opened
>3) An image is output to the ServletOutputStream
>4) The ServletOutputStream is closed
>5) The arguments are processed by our servlet and logged to
> a database.
>The servlet is coded in such a way that the processing
>before the image is output and the outputstream is closed
>is minimal.
>I'd think that the servlet *should* return a response very
>quickly.
>Due to the fact that it's taking a long time, I'm wondering
>what closes the connection to an end-user's web
>browser. I'm inclined to think that
> ServletOutputStream.close()
>doesn't do it.
You'd be right, but that doesn't actually effect the browser.
>If not, how can we speed this up and still get our
>processing done?
>In order to close the connection to a browser as fast
>as possible, do we have to set the ServletResponse
>object == null or something to force it to go out of
>scope?
A TCP connection is invisible to a servlet but here is a slightly
simplified description of how containers generally do it.
1. container recieves connection from UA
2. container creates a thread to serve the request and passes the TCP
socket to the thread
- from here eveything happens in the container's request thread -
3. container creates request and response objects (which implement
HttpServletRequest and HttpServletResponse)
4. container opens the socket-inputstream and socket-outputstream and
gives handles to the request and response objects
5. request objects reads the HTTP header from the socket-inputstream
6. request object asks the container for a RequestDispatcher pointing
to the servlet relevant to the URI specified in the request
7. request object calls .forward() on the RequestDispatcher passing
it the request and response objects
8. RD calls the servlet with the request and response objects
9. servlets does: getOutputStream() (or getWriter() it's the same
thing as far as this is concerned)
10. request wraps the socket-outputstream in a ServletOutputStream
(or a PrintWriter for the getWriter() call)
11. servlet does some output
12. container writes the header of the response to the
socket-outputstream and then the output
13. servlet does some more output
14. servlet calls outputstream.close()
15. the close() percolates through the wrappers to the
socket-outputstream.
16. the socket-outputstream is flushed
17. the servlet returns
18. the container calls socket-inputstream.close() which closes the
connection
19. the request thread ends and is GCed
So that's basically it. But there are some well understood tricks to
improve performance.
The first is that HTTP persistent-connections might be used; a
persistent connection is designed to allow multiple requests to be
"pipelined" over one socket.
To achieve this, when it comes to 10. the container provides a
wrapper of the socket-outputstream that does not call the
socket-outputstream close() when the servlet calls
servlet-outputstream.close().
If the servlet was allowed to close() the socket-outputstream then
the stream would disappear and the persistent-connection would not be
able to write to the socket.
When servlet-outputstream.close() is called a signal is sent to the
browser that that the response is complete (through chunked-encoding
system for those of you interested).
The container decides when the socket should actually be closed. It
does this by having a timeout on the wrapper for the
socket-inputstream... when the timeout runs out both streams are
closed and the connection disappears.
A second performance trick is to buffer output.
At step 10. the request might wrap the socket-outputstream in an
outputstream that buffers output.
The wrapper has a buffer limit and when the output from the servlet
reaches the limit then step 12 is performed and then the buffer
written.
If the servlet doesn't write enough data to fill the buffer (this is
the ideal solution) then the output is done at step 14. (when the
servlet calls servlet-outputstream.close()). The header is written
first and then the buffered content.
Note: this is quite a clever system as it allows containers to set
the content-length of your response without you doing anything!
ow you understand that there are some other important HTTP
considerations:
1. a browser can know that the response is "finished" only if the
"content-length" header value is set to tell it how long the response
is (otherwsie it has no way of knowing).
2. if content-length is NOT set then the browser will only consider
the connection closed when the server drops the connection.
(HTTP/1.1 does solve this with persistent-connections and chunked
encoding)
A lot depends on how you write your servlet. Yesterday's posts on
OutputStream's explained some of the problems with using some methods
of ServletOutputStream but it is possible to write bad code that is
not the fault of ServletOutputStream.
For example: to output an image how are you writing the data?
Many people do this:
OutputStream out=response.getOutputStream();
FileInputStream fin=new FileInputStream("myimage.jpg");
byte b=find.read();
while(b!=-1)
{
out.write(b);
b=fin.read();
}
That is *very* expensive because the JVM is having to do a lot of
processing. You need to dispatch as much output as possible at once,
eg:
File f=new File("myimage.jpg");
int size=f.size();
byte[] arr=new byte[size];
FileInputStream fin=new FileInputStream(f);
fin.read(arr,0,size);
OutputStream out=response.getOutputStream();
out.write(arr,0,size);
What I said yesterday about ServletOutputStream still counts of
course, unless it provides a good implementation of
write(byte[],int,int) then the code above will end up as slow as the
first example.
And unless you're using a container that does buffered output (or
chunked-encoding) the browser will have no way of knowing that the
image has finished downloading. Therefore we re-write the above so
that it looks like this:
File f=new File("myimage.jpg");
int size=f.size();
response.setContentLength(size);
byte[] arr=new byte[size];
FileInputStream fin=new FileInputStream(f);
fin.read(arr,0,size);
OutputStream out=response.getOutputStream();
out.write(arr,0,size);
Now, when the browser recieves the header it will know that it has to
read exactly "size" bytes and then close the connection.
So: in conclusion: ServletOutputStream.close() does close the
connection if everything is set up correctly.
Setting "content-length" to the size of your image, making sure you
use low-impedance methods on the streams will probably improve your
performance.
And that's the last planned keynote from "OutputStrean Expo" - join
us next year to find out how OutputStream's can improve your
performance on quiz shows.
Nic
___________________________________________________________________________
To unsubscribe, send email to [EMAIL PROTECTED] and include in the body
of the message "signoff SERVLET-INTEREST".
Archives: http://archives.java.sun.com/archives/servlet-interest.html
Resources: http://java.sun.com/products/servlet/external-resources.html
LISTSERV Help: http://www.lsoft.com/manuals/user/user.html