mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-04-26 12:58:03 +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:
parent
9d7abda787
commit
ca1027c22f
@ -2895,6 +2895,20 @@ tune.h2.be.initial-window-size <number>
|
|||||||
between clients. It doesn't affect resource usage.
|
between clients. It doesn't affect resource usage.
|
||||||
See also: tune.h2.initial-window-size.
|
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>
|
tune.h2.fe.initial-window-size <number>
|
||||||
Sets the HTTP/2 initial window size for incoming connections, which is the
|
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
|
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
|
clients to experience a lack of responsiveness if pages are accessed in
|
||||||
parallel to large uploads. See also: tune.h2.initial-window-size.
|
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>
|
tune.h2.header-table-size <number>
|
||||||
Sets the HTTP/2 dynamic header table size. It defaults to 4096 bytes and
|
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
|
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.be.initial-window-size.
|
||||||
|
|
||||||
tune.h2.max-concurrent-streams <number>
|
tune.h2.max-concurrent-streams <number>
|
||||||
Sets the HTTP/2 maximum number of concurrent streams per connection (ie the
|
Sets the default HTTP/2 maximum number of concurrent streams per connection
|
||||||
number of outstanding requests on a single connection). The default value is
|
(i.e. the number of outstanding requests on a single connection). Ths value
|
||||||
100. A larger one may slightly improve page load time for complex sites when
|
is used for incoming connections when tune.h2.fe.max-concurrent-streams is
|
||||||
visited over high latency networks, but increases the amount of resources a
|
not set, and for outgoing connections when tune.h2.be.max-concurrent-streams
|
||||||
single client may allocate. A value of zero disables the limit so a single
|
is not set. The default value is 100. The impact varies depending on the side
|
||||||
client may create as many streams as allocatable by HAProxy. It is highly
|
so please see the two settings above for more details. It is recommended not
|
||||||
recommended not to change this value.
|
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>
|
tune.h2.max-frame-size <number>
|
||||||
Sets the HTTP/2 maximum frame size that HAProxy announces it is willing to
|
Sets the HTTP/2 maximum frame size that HAProxy announces it is willing to
|
||||||
|
52
src/mux_h2.c
52
src/mux_h2.c
@ -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_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_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 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 */
|
static int h2_settings_max_frame_size = 0; /* unset */
|
||||||
|
|
||||||
/* a dummy closed endpoint */
|
/* a dummy closed endpoint */
|
||||||
@ -572,6 +574,23 @@ static inline int h2c_may_expire(const struct h2c *h2c)
|
|||||||
return !h2c->nb_sc;
|
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 */
|
/* update h2c timeout if needed */
|
||||||
static void h2c_update_timeout(struct h2c *h2c)
|
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 */
|
/* 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)
|
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
|
/* 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. */
|
/* Initialise the context. */
|
||||||
h2c->st0 = H2_CS_PREFACE;
|
h2c->st0 = H2_CS_PREFACE;
|
||||||
h2c->conn = conn;
|
h2c->conn = conn;
|
||||||
h2c->streams_limit = h2_settings_max_concurrent_streams;
|
h2c->streams_limit = h2c_max_concurrent_streams(h2c);
|
||||||
h2c->max_id = -1;
|
h2c->max_id = -1;
|
||||||
h2c->errcode = H2_ERR_NO_ERROR;
|
h2c->errcode = H2_ERR_NO_ERROR;
|
||||||
h2c->rcvd_c = 0;
|
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);
|
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);
|
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;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -1645,6 +1664,7 @@ static int h2c_send_settings(struct h2c *h2c)
|
|||||||
struct buffer buf;
|
struct buffer buf;
|
||||||
int iws;
|
int iws;
|
||||||
int mfs;
|
int mfs;
|
||||||
|
int mcs;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
TRACE_ENTER(H2_EV_TX_FRAME|H2_EV_TX_SETTINGS, h2c->conn);
|
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);
|
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 */
|
char str[6] = "\x00\x03"; /* max_concurrent_streams */
|
||||||
|
|
||||||
/* Note: 0 means "unlimited" for haproxy's config but not for
|
/* Note: 0 means "unlimited" for haproxy's config but not for
|
||||||
* the protocol, so never send this value!
|
* 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);
|
chunk_memcat(&buf, str, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2247,8 +2268,8 @@ static int h2c_handle_settings(struct h2c *h2c)
|
|||||||
case H2_SETTINGS_MAX_CONCURRENT_STREAMS:
|
case H2_SETTINGS_MAX_CONCURRENT_STREAMS:
|
||||||
if (h2c->flags & H2_CF_IS_BACK) {
|
if (h2c->flags & H2_CF_IS_BACK) {
|
||||||
/* the limit is only for the backend; for the frontend it is our limit */
|
/* the limit is only for the backend; for the frontend it is our limit */
|
||||||
if ((unsigned int)arg > h2_settings_max_concurrent_streams)
|
if ((unsigned int)arg > h2c_max_concurrent_streams(h2c))
|
||||||
arg = h2_settings_max_concurrent_streams;
|
arg = h2c_max_concurrent_streams(h2c);
|
||||||
h2c->streams_limit = arg;
|
h2c->streams_limit = arg;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -6924,16 +6945,23 @@ static int h2_parse_initial_window_size(char **args, int section_type, struct pr
|
|||||||
return 0;
|
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,
|
static int h2_parse_max_concurrent_streams(char **args, int section_type, struct proxy *curpx,
|
||||||
const struct proxy *defpx, const char *file, int line,
|
const struct proxy *defpx, const char *file, int line,
|
||||||
char **err)
|
char **err)
|
||||||
{
|
{
|
||||||
|
uint *vptr;
|
||||||
|
|
||||||
if (too_many_args(1, args, err, NULL))
|
if (too_many_args(1, args, err, NULL))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
h2_settings_max_concurrent_streams = atoi(args[1]);
|
/* backend/frontend/default */
|
||||||
if ((int)h2_settings_max_concurrent_streams < 0) {
|
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]);
|
memprintf(err, "'%s' expects a positive numeric value.", args[0]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -6993,7 +7021,9 @@ INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_h2);
|
|||||||
/* config keyword parsers */
|
/* config keyword parsers */
|
||||||
static struct cfg_kw_list cfg_kws = {ILH, {
|
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.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.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.header-table-size", h2_parse_header_table_size },
|
||||||
{ CFG_GLOBAL, "tune.h2.initial-window-size", h2_parse_initial_window_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 },
|
{ CFG_GLOBAL, "tune.h2.max-concurrent-streams", h2_parse_max_concurrent_streams },
|
||||||
|
Loading…
Reference in New Issue
Block a user