[MEDIUM] implement option tcp-smart-accept at the frontend

This option disables TCP quick ack upon accept. It is also
automatically enabled in HTTP mode, unless the option is
explicitly disabled with "no option tcp-smart-accept".

This saves one packet per connection which can bring reasonable
amounts of bandwidth for servers processing small requests.
This commit is contained in:
Willy Tarreau 2009-06-14 12:07:01 +02:00
parent 3842f00a19
commit 9ea05a790f
5 changed files with 51 additions and 0 deletions

View file

@ -706,6 +706,8 @@ option smtpchk X - X X
[no] option splice-response X X X X
[no] option srvtcpka X - X X
option ssl-hello-chk X - X X
[no] option tcp-smart-
accept X X X -
option tcpka X X X X
option tcplog X X X X
[no] option tcpsplice X X X X
@ -2755,6 +2757,39 @@ option ssl-hello-chk
See also: "option httpchk"
option tcp-smart-accept
no option tcp-smart-accept
Enable or disable the saving of one ACK packet during the accept sequence
May be used in sections : defaults | frontend | listen | backend
yes | yes | yes | no
Arguments : none
When an HTTP connection request comes in, the system acknowledges it on
behalf of HAProxy, then the client immediately sends its request, and the
system acknowledges it too while it is notifying HAProxy about the new
connection. HAProxy then reads the request and responds. This means that we
have one TCP ACK sent by the system for nothing, because the request could
very well be acknowledged by HAProxy when it sends its response.
For this reason, in HTTP mode, HAProxy automatically asks the system to avoid
sending this useless ACK on platforms which support it (currently at least
Linux). It must not cause any problem, because the system will send it anyway
after 40 ms if the response takes more time than expected to come.
During complex network debugging sessions, it may be desirable to disable
this optimization because delayed ACKs can make troubleshooting more complex
when trying to identify where packets are delayed. It is then possible to
fall back to normal behaviour by specifying "no option tcp-smart-accept".
It is also possible to force it for non-HTTP proxies by simply specifying
"option tcp-smart-accept". For instance, it can make sense with some services
such as SMTP where the server speaks first.
It is recommended to avoid forcing this option in a defaults section. In case
of doubt, consider setting it back to automatic values by prepending the
"default" keyword before it, or disabling it using the "no" keyword.
option tcpka
Enable or disable the sending of TCP keepalive packets on both sides
May be used in sections : defaults | frontend | listen | backend

View file

@ -68,6 +68,7 @@
#define LI_O_NONE 0x0000
#define LI_O_NOLINGER 0x0001 /* disable linger on this socket */
#define LI_O_FOREIGN 0x0002 /* permit listening on foreing addresses */
#define LI_O_NOQUICKACK 0x0004 /* disable quick ack of immediate data (linux) */
/* The listener will be directly referenced by the fdtab[] which holds its
* socket. The listener provides the protocol-specific accept() function to

View file

@ -117,6 +117,7 @@
#define PR_O2_RSPBUG_OK 0x00000010 /* let buggy responses pass through */
#define PR_O2_NOLOGNORM 0x00000020 /* don't log normal traffic, only errors and retries */
#define PR_O2_LOGERRORS 0x00000040 /* log errors and retries at level LOG_ERR */
#define PR_O2_SMARTACC 0x00000080 /* don't immediately ACK request after accept */
/* This structure is used to apply fast weighted round robin on a server group */
struct fwrr_group {

View file

@ -135,6 +135,7 @@ static const struct cfg_opt cfg_opts2[] =
{ "accept-invalid-http-response", PR_O2_RSPBUG_OK, PR_CAP_BE, 0 },
{ "dontlog-normal", PR_O2_NOLOGNORM, PR_CAP_FE, 0 },
{ "log-separate-errors", PR_O2_LOGERRORS, PR_CAP_FE, 0 },
{ "tcp-smart-accept", PR_O2_SMARTACC, PR_CAP_FE, 0 },
{ NULL, 0, 0, 0 }
};
@ -3741,6 +3742,12 @@ int readcfgfile(const char *file)
if (curproxy->mode == PR_MODE_HTTP)
listener->analysers |= AN_REQ_HTTP_HDR;
/* smart accept mode is automatic in HTTP mode */
if ((curproxy->options2 & PR_O2_SMARTACC) ||
(curproxy->mode == PR_MODE_HTTP &&
!(curproxy->no_options2 & PR_O2_SMARTACC)))
listener->options |= LI_O_NOQUICKACK;
if (curproxy->tcp_req.inspect_delay ||
!LIST_ISEMPTY(&curproxy->tcp_req.inspect_rules))
listener->analysers |= AN_REQ_INSPECT;

View file

@ -18,6 +18,8 @@
#include <string.h>
#include <time.h>
#include <netinet/tcp.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
@ -263,6 +265,11 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
goto tcp_close_return;
}
#ifdef TCP_QUICKACK
if (listener->options & LI_O_NOQUICKACK)
setsockopt(fd, SOL_TCP, TCP_QUICKACK, (char *) &zero, sizeof(zero));
#endif
/* the socket is ready */
listener->fd = fd;
listener->state = LI_LISTEN;