1
0
mirror of http://git.haproxy.org/git/haproxy.git/ synced 2025-03-31 23:58:16 +00:00

MINOR: mux-h2: make the max number of concurrent streams configurable per side

For a long time the maximum number of concurrent streams was set once for
both sides (front and back) while the impacts are different. This commit
allows it to be configured separately for each side. The older settings
remains the fallback choice when other ones are not set.
This commit is contained in:
Willy Tarreau 2023-04-18 15:57:03 +02:00
parent 9d7abda787
commit ca1027c22f
2 changed files with 74 additions and 18 deletions

View File

@ -2895,6 +2895,20 @@ tune.h2.be.initial-window-size <number>
between clients. It doesn't affect resource usage.
See also: tune.h2.initial-window-size.
tune.h2.be.max-concurrent-streams <number>
Sets the HTTP/2 maximum number of concurrent streams per outgoing connection
(i.e. the number of outstanding requests on a single connection to a server).
When not set, the default set by tune.h2.max-concurrent-streams applies. A
smaller value than the default 100 may improve a site's responsiveness at the
expense of maintaining more established connections to the servers. When the
"http-reuse" setting is set to "always", it is recommended to reduce this
value so as not to mix too many different clients over the same connection,
because if a client is slower than others, a mechanism known as "head of
line blocking" tends to cause cascade effect on download speed for all
clients sharing a connection (keep tune.h2.be.initial-window-size low in this
case). It is highly recommended not to increase this value; some might find
it optimal to run at low values (1..5 typically).
tune.h2.fe.initial-window-size <number>
Sets the HTTP/2 initial window size for incoming connections, which is the
number of bytes the client can upload before waiting for an acknowledgment
@ -2907,6 +2921,16 @@ tune.h2.fe.initial-window-size <number>
clients to experience a lack of responsiveness if pages are accessed in
parallel to large uploads. See also: tune.h2.initial-window-size.
tune.h2.fe.max-concurrent-streams <number>
Sets the HTTP/2 maximum number of concurrent streams per incoming connection
(i.e. the number of outstanding requests on a single connection from a
client). When not set, the default set by tune.h2.max-concurrent-streams
applies. A larger value than the default 100 may sometimes slightly improve
the page load time for complex sites with lots of small objects over high
latency networks but can also result in using more memory by allowing a
client to allocate more resources at once. The default value of 100 is
generally good and it is recommended not to change this value.
tune.h2.header-table-size <number>
Sets the HTTP/2 dynamic header table size. It defaults to 4096 bytes and
cannot be larger than 65536 bytes. A larger value may help certain clients
@ -2927,13 +2951,15 @@ tune.h2.initial-window-size <number>
tune.h2.be.initial-window-size.
tune.h2.max-concurrent-streams <number>
Sets the HTTP/2 maximum number of concurrent streams per connection (ie the
number of outstanding requests on a single connection). The default value is
100. A larger one may slightly improve page load time for complex sites when
visited over high latency networks, but increases the amount of resources a
single client may allocate. A value of zero disables the limit so a single
client may create as many streams as allocatable by HAProxy. It is highly
recommended not to change this value.
Sets the default HTTP/2 maximum number of concurrent streams per connection
(i.e. the number of outstanding requests on a single connection). Ths value
is used for incoming connections when tune.h2.fe.max-concurrent-streams is
not set, and for outgoing connections when tune.h2.be.max-concurrent-streams
is not set. The default value is 100. The impact varies depending on the side
so please see the two settings above for more details. It is recommended not
to use this setting and to switch to the per-side ones instead. A value of
zero disables the limit so a single client may create as many streams as
allocatable by HAProxy. It is highly recommended not to change this value.
tune.h2.max-frame-size <number>
Sets the HTTP/2 maximum frame size that HAProxy announces it is willing to

View File

@ -406,7 +406,9 @@ static int h2_settings_header_table_size = 4096; /* initial value */
static int h2_settings_initial_window_size = 65536; /* default initial value */
static int h2_be_settings_initial_window_size = 0; /* backend's default initial value */
static int h2_fe_settings_initial_window_size = 0; /* frontend's default initial value */
static unsigned int h2_settings_max_concurrent_streams = 100;
static unsigned int h2_settings_max_concurrent_streams = 100; /* default value */
static unsigned int h2_be_settings_max_concurrent_streams = 0; /* backend value */
static unsigned int h2_fe_settings_max_concurrent_streams = 0; /* frontend value */
static int h2_settings_max_frame_size = 0; /* unset */
/* a dummy closed endpoint */
@ -572,6 +574,23 @@ static inline int h2c_may_expire(const struct h2c *h2c)
return !h2c->nb_sc;
}
/* returns the number of max concurrent streams permitted on a connection,
* depending on its side (frontend or backend), falling back to the default
* h2_settings_max_concurrent_streams. It may even be zero.
*/
static inline int h2c_max_concurrent_streams(const struct h2c *h2c)
{
int ret;
ret = (h2c->flags & H2_CF_IS_BACK) ?
h2_be_settings_max_concurrent_streams :
h2_fe_settings_max_concurrent_streams;
ret = ret ? ret : h2_settings_max_concurrent_streams;
return ret;
}
/* update h2c timeout if needed */
static void h2c_update_timeout(struct h2c *h2c)
{
@ -715,7 +734,7 @@ static inline void h2c_restart_reading(const struct h2c *h2c, int consider_buffe
/* returns true if the front connection has too many stream connectors attached */
static inline int h2_frt_has_too_many_sc(const struct h2c *h2c)
{
return h2c->nb_sc > h2_settings_max_concurrent_streams;
return h2c->nb_sc > h2c_max_concurrent_streams(h2c);
}
/* Tries to grab a buffer and to re-enable processing on mux <target>. The h2c
@ -1001,7 +1020,7 @@ static int h2_init(struct connection *conn, struct proxy *prx, struct session *s
/* Initialise the context. */
h2c->st0 = H2_CS_PREFACE;
h2c->conn = conn;
h2c->streams_limit = h2_settings_max_concurrent_streams;
h2c->streams_limit = h2c_max_concurrent_streams(h2c);
h2c->max_id = -1;
h2c->errcode = H2_ERR_NO_ERROR;
h2c->rcvd_c = 0;
@ -1528,7 +1547,7 @@ static struct h2s *h2c_frt_stream_new(struct h2c *h2c, int id, struct buffer *in
TRACE_ENTER(H2_EV_H2S_NEW, h2c->conn);
if (h2c->nb_streams >= h2_settings_max_concurrent_streams) {
if (h2c->nb_streams >= h2c_max_concurrent_streams(h2c)) {
TRACE_ERROR("HEADERS frame causing MAX_CONCURRENT_STREAMS to be exceeded", H2_EV_H2S_NEW|H2_EV_RX_FRAME|H2_EV_RX_HDR, h2c->conn);
goto out;
}
@ -1645,6 +1664,7 @@ static int h2c_send_settings(struct h2c *h2c)
struct buffer buf;
int iws;
int mfs;
int mcs;
int ret = 0;
TRACE_ENTER(H2_EV_TX_FRAME|H2_EV_TX_SETTINGS, h2c->conn);
@ -1685,13 +1705,14 @@ static int h2c_send_settings(struct h2c *h2c)
chunk_memcat(&buf, str, 6);
}
if (h2_settings_max_concurrent_streams != 0) {
mcs = h2c_max_concurrent_streams(h2c);
if (mcs != 0) {
char str[6] = "\x00\x03"; /* max_concurrent_streams */
/* Note: 0 means "unlimited" for haproxy's config but not for
* the protocol, so never send this value!
*/
write_n32(str + 2, h2_settings_max_concurrent_streams);
write_n32(str + 2, mcs);
chunk_memcat(&buf, str, 6);
}
@ -2247,8 +2268,8 @@ static int h2c_handle_settings(struct h2c *h2c)
case H2_SETTINGS_MAX_CONCURRENT_STREAMS:
if (h2c->flags & H2_CF_IS_BACK) {
/* the limit is only for the backend; for the frontend it is our limit */
if ((unsigned int)arg > h2_settings_max_concurrent_streams)
arg = h2_settings_max_concurrent_streams;
if ((unsigned int)arg > h2c_max_concurrent_streams(h2c))
arg = h2c_max_concurrent_streams(h2c);
h2c->streams_limit = arg;
}
break;
@ -6924,16 +6945,23 @@ static int h2_parse_initial_window_size(char **args, int section_type, struct pr
return 0;
}
/* config parser for global "tune.h2.max-concurrent-streams" */
/* config parser for global "tune.h2.{be.,fe.,}max-concurrent-streams" */
static int h2_parse_max_concurrent_streams(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
uint *vptr;
if (too_many_args(1, args, err, NULL))
return -1;
h2_settings_max_concurrent_streams = atoi(args[1]);
if ((int)h2_settings_max_concurrent_streams < 0) {
/* backend/frontend/default */
vptr = (args[0][8] == 'b') ? &h2_be_settings_max_concurrent_streams :
(args[0][8] == 'f') ? &h2_fe_settings_max_concurrent_streams :
&h2_settings_max_concurrent_streams;
*vptr = atoi(args[1]);
if ((int)*vptr < 0) {
memprintf(err, "'%s' expects a positive numeric value.", args[0]);
return -1;
}
@ -6993,7 +7021,9 @@ INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_h2);
/* config keyword parsers */
static struct cfg_kw_list cfg_kws = {ILH, {
{ CFG_GLOBAL, "tune.h2.be.initial-window-size", h2_parse_initial_window_size },
{ CFG_GLOBAL, "tune.h2.be.max-concurrent-streams", h2_parse_max_concurrent_streams },
{ CFG_GLOBAL, "tune.h2.fe.initial-window-size", h2_parse_initial_window_size },
{ CFG_GLOBAL, "tune.h2.fe.max-concurrent-streams", h2_parse_max_concurrent_streams },
{ CFG_GLOBAL, "tune.h2.header-table-size", h2_parse_header_table_size },
{ CFG_GLOBAL, "tune.h2.initial-window-size", h2_parse_initial_window_size },
{ CFG_GLOBAL, "tune.h2.max-concurrent-streams", h2_parse_max_concurrent_streams },