Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 7
Asynchronous message handling can improve application performance by avoiding waiting threads, but another performance bottleneck arises when reading the message content.
It is not unusual for an HTTP message to contain kilobytes of content data. On the transport level, such larger messages will be broken down into several TCP segments. The TCP segment size is limited and depends on the underlying network and link layer. For Ethernet-based networks the maximum TCP segment size is up to 1460 bytes.
Bodyless HTTP messages such as GET requests don't contain body data. Often the size of such bodyless messages is smaller than 1 kilobyte. Listing 3 shows a
simple HTTP request.
GET /html/rfc2616.html HTTP/1.1
Host: tools.ietf.org:80
User-Agent: xSocket-http/2.0-alpha-3
The correlating response of the request shown above contains a message body of 0.5 megabytes. On a personal Internet connection, the response message shown in Listing 4 would be broken into several TCP segments when sent.
HTTP/1.1 200 OK
Content-Length: 509497
Accept-Ranges: bytes
Last-Modified: Tue, 20 Nov 2007 03:10:57 GMT
Date: Sun, 03 Feb 2008 09:46:31 GMT
Content-Type: text/html; charset=US-ASCII
ETag: "d4026-7c639-9d13d240"
Server: Apache/2.2.6 (Debian) DAV/2 SVN/1.4.4 mod_python/3.3.1 Python/2.4.4 mod_ssl/2.2.6 OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
<meta name="robots" content="index,follow" />
<meta name="creator" content="rfcmarkup version 1.53" />
<link rel="icon" href="/images/rfc.png" type="image/png" />
<link rel="shortcut icon" href="/images/rfc.png" type="image/png" />
<title>RFC 2616 Hypertext Transfer Protocol -- HTTP/1.1</title>
[...]
</small></small></span>
</body></html>
Data transfer fragmentation can be hidden at the API level by accessing the body data as a steady and continuous stream. This approach, known as streaming, avoids the need to buffer large chunks of data before processing it. Streaming can also reduce the latency of HTTP calls, especially if both peers support streaming.
Using streaming allows the receiver to start processing the message content before the entire message has been transmitted. Often an HTTP message contains unstructured or semi-structured data, such as HTML pages, video, or music files, which will be processed immediately by the receiving peer. For instance, most browsers start rendering and displaying HTML pages without waiting for the complete page. For this reason most HTTP libraries support a stream-based API to access the message content.
In contrast to the body data, the message header contains well-structured data entries. To access the message header data most HTTP libraries provide dedicated and typed setter and getter methods. In most use cases the header can only be processed after the complete header has been received. The HTTP/1.1 specification doesn't define the order of the message headers, though it does state that it's a good practice to send general-header fields first, followed by request-header or response-header fields, and ending with the entity-header fields.
To process received body data in a streaming manner, the receiving peer has to be notified immediately after the message header has been received. Based on the message header information, the receiver is able to determine the type of the received HTTP message, if body data exists, and which type of content the body contains.
The example code in Listing 5 (below) streams a returned HTML page into a file. The response message data will be processed
as soon as it appears. Based on the retrieved body channel the FileChannel's transferFrom() implementation calls the body channel's read() method to transfer the data into the filesystem. This occurs in a blocking manner. If the socket read buffer is empty, the
body channel's read() method will block until more data is received or the end-of-stream is reached. Blocking the read operation suspends the current
caller thread, which can lead to inefficiency in system resource usage.
HttpClient httpClient = new HttpClient();
HttpRequest req = new HttpRequest("GET", "http://tools.ietf.org/html/rfc2616.html");
// returns immediately if the complete header (not message!) is received
HttpResponse resp = httpClient.call(req);
if (resp.getStatus() == 200) {
// create the output file
File file = new File("rfc2616.html");
file.createNewFile();
FileChannel fc = new RandomAccessFile(file, "rw").getChannel();
// get a blocking message body channel
ReadableByteChannel inputBodyChannel = resp.getBlockingBody();
// and transfer the data
fc.transferFrom(inputBodyChannel, 0, 900000);
fc.close();
}
// ...
To process the message body in a non-blocking mode, a handler similar to the one seen in the asynchronous message calling
example from Listing 2 can be used. In this case, a non-blocking body channel will be retrieved instead of a blocking channel.
In contrast to the blocking channel the non-blocking channel's read() methods return immediately, whether data has been acquired or not. Notification support is required to avoid repeated, unsuccessful
reads within a loop.
The BodyToFileStreamer of the example code in Listing 6 implements such a notification callback method. After retrieving the non-blocking body channel,
the body handler will be assigned to the channel. The setDataHandler() call returns immediately. Setting the handler ensures that the body channel checks whether data is already available. If
data is available, the handler's onData() method is run.
The callback method is also called each time body data is available. The network library takes a (pooled) worker thread to perform the callback method. This thread is only assigned to the body channel as long as the callback method is executed. For this reason no outstanding threads are required.
HttpClient httpClient = new HttpClient();
HttpRequest req = new HttpRequest("GET", "http://tools.ietf.org/html/rfc2616.html");
// returns immediately if the complete header (not message!) is received
HttpResponse resp = httpClient.call(req);
if (resp.getStatus() == 200) {
// create the output file
final File file = new File("rfc2616.html");
file.createNewFile();
final FileChannel fc = new RandomAccessFile(file, "rw").getChannel();
// get a non blocking message body channel
NonBlockingBodyDataSource nbInputBodyChannel = resp.getNonBlockingBody();
// data handler
IBodyDataHandler bodyToFileStreamer = new IBodyDataHandler() {
public boolean onData(NonBlockingBodyDataSource bodyChannel) {
try {
int available = bodyChannel.available();
// data to transfer?
if (available > 0) {
bodyChannel.transferTo(fc, available);
// end of stream reached?
} else if (available == -1) {
fc.close();
}
} catch (IOException ioe) {
file.delete();
}
return true;
}
// ...
};
// set the data handler
nbInputBodyChannel.setDataHandler(bodyToFileStreamer);
}
// ...
xSocket.
Archived Discussions (Read only)