The default HTTP connection is usually closed after each request has been completed, meaning that the server closes the TCP connection after delivering the response. In order to keep the connection open for multiple requests, the keep-alive connection header can be used. All modern browsers use persistent connections as long as the server is willing to cooperate. You may also use HTTP/1.1, where Keep-Alive is implemented differently and the connections are kept open by default unless the response contains a “Connection: close” header.
Defeating HTTP/1.1’s default Keep-Alive behavior is a bad practice for regular HTTP connections but it’s far worse for HTTPS connections because the initial setup costs of a HTTPS connection are far higher than a regular HTTP connection.
How HTTP Works Without Keep-Alive
- A client has to create a new connection to interact and receive a file from a server
- The client makes a request using a new connection and this connection terminates after receiving the file
- The browser interprets the response and checks whether any other files are required to display the complete web page
- After a thorough analysis, it creates a new connection to request each of those files
But this mechanism is very inefficient, especially with complex web pages that have a large number of elements.
Instead, if Keep-Alive is permitted, the client will reuse the existing connection instead of recreating new ones.
Benefits Of Keep-Alive
- Lower CPU and memory usage (because fewer connections are opened simultaneously).
- Enables HTTP pipelining of requests and responses.
- Reduced network congestion (fewer TCP connections).
- Reduced latency in subsequent requests (no handshaking).
- Errors can be reported without the penalty of closing the TCP connection.
These advantages are even more important for secure HTTPS connections, because establishing a secure connection needs much more CPU time and network round-trips.
If the client does not close the connection when all of the data it needs has been received, the resources needed to keep the connection open on the server will be unavailable for other clients. How much this affects the server’s availability and how long the resources are unavailable depend on the server’s architecture and configuration.
Load Test Perspective
The Keep-Alive management is an important parameter which has to be taken into account when using Simulators (ie Neoload/Jmeter/Gatling/ab…), too often, people don’t pay attention.
In a load test scenario, how the two cases will be related to each other?
In any case the tester (Neoload), will start it’s timer when initiating the request, and stop it, when the response is received. This means that in “no Keep-Alive” case, it will start at the connection creation and stop at the closing. In the “Keep-Alive” case, it will start at the first byte sent for the request, and stop at the last byte.
On his side, Apache starts its measuring when the request is assigned to a worker, and stop when the last byte is sent, releasing the worker.
When Apache process a request, it’s first accepted on its listen queue, waiting for a worker to be free to handle it. In the case where you have keep-alive, this worker is kept busy until the connection is closed, and not just after the request is done.
So, if your scenari don’t reuse connections, you risk opening up a lot of connections but keeping many of them busy until they expire, meaning that you may not have enough connections and lots of them will pie up in the Apache listen queue waiting for the work done. From a tester point-of-view, this means that the clock is running, while the Apache one is not! At the end you will have a discrepancy between what Apache see, and what, you, the tester see! One way to avoid this, is to rise the number of Apache worker, of course. But, in some setup, most of the time, the worker will handle the work to JBOSS or Tomcat through an AJP connection, so you will need to rise the JBOSS poll also! The other, clean, way, is to explicitly close the connection from the server, if we know that there will be no reusing.
Once you know better how it works, you will want to tune this parameter! The default connection timeout of Apache is only 5 seconds for Apache httpd 2.2 and above. The advantage of a short timeout is the ability to deliver multiple components of a web page quickly while not consuming resources to run multiple server processes or threads for too long.
Properties That Affect Keep-Alive Functionality
Use “KeepAlive On” to enable it.
To disable, just use “KeepAlive Off”.
It sets the maximum number of requests for every Keep-Alive connection.
A value of 100 is normally good enough for almost any scenario. This value, however, should be increased depending on the amount of files within a web page that the server is supposed to deliver.
This setting prevents unused connections from hanging around for too long. It sets how long your server should wait for new requests from clients.
note: Do not confuse http Keep-Alive requests with the KeepAliveTimeout on the HTTP and AJP connectors. This timeout is defined as: KeepAliveTimeout sets the number of seconds the server is waiting after a request has been served before it closes the connection. Once the server receives a request, the Timeout directive applies instead. The default value is to use the value that has been set for the connectionTimeout attribute, which is really for internally cleaning up the ThreadPool in JBoss Web.
In Apache Httpd you can follow the keep-alive usage by using the %k in LogFormat directive.
%k : Number of keepalive requests handled on this connection. Interesting if KeepAlive is being used, so that, for example, a ‘1’ means the first Keep-Alive request after the initial one, ‘2’ the second, etc…; otherwise this is always 0 (indicating the initial request). Available in versions 2.2.11 and later.
For those of you, who have a Big-IP F5 as load balancer in front of their servers, you can note that when the BIG-IP system passes an HTTP response from the server to the client, the BIG-IP system will attempt to preserve the server’s method of indicating the end of the response by using an HTTP Content-Length header.
HTTP Keep-alive timeout is mostly controlled by the server.
Example, Server says, if you don’t send me a request, after 15 seconds of idleness, I will close the connection. When server closes the connection, the TCP idle timeout is irrelevant, because connection is explicitly being closed. So in effect, http keep-alive timeout overrides the TCP one. If connection is closed for any reason, then client must initiate a new connection to send a new request.
The TCP idle timeout only takes effect if the connection is idle (no data transmitted) and the connection was not closed for any other reason (such as http keep-alive timeout). You can think of it as the default timeout when nothing else closes the connection.
In conclusion, we can see that HTTP Keep-Alive take a major part in our application performance and we should take care of it instead of letting the default do it. In particular, if possible, we should make our application handle it as close as possible.