Upstream: enabled keepalive by default for explicit upstreams.

Keepalive is now automatically enabled in the "local" mode for upstreams
defined in configuration files. Cached keepalive connections are no longer
shared between different locations referencing the same explicit upstream
unless keepalive is explicitly configured without the "local" parameter.

To disable keepalive entirely, use keepalive 0; inside the upstream block.
To allow sharing cached connections between locations, configure
keepalive <max_cached>; without the "local" parameter.
This commit is contained in:
Roman Semenov authored and Roman Arutyunyan committed 2026-03-24 14:28:52 +04:00
1 parent c5d36eac33
commit 4fbe4b6274
1 file changed
+67 -63
@@ -19,7 +19,6 @@ typedef struct {
ngx_queue_t cache;
ngx_queue_t free;
ngx_http_upstream_init_pt original_init_upstream;
ngx_http_upstream_init_peer_pt original_init_peer;
ngx_uint_t local; /* unsigned local:1; */
@@ -83,6 +82,8 @@ static void ngx_http_upstream_notify_keepalive_peer(ngx_peer_connection_t *pc,
void *data, ngx_uint_t type);
static void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf);
static char *ngx_http_upstream_keepalive_init_main_conf(ngx_conf_t *cf,
void *conf);
static char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
@@ -126,7 +127,7 @@ static ngx_http_module_t ngx_http_upstream_keepalive_module_ctx = {
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
ngx_http_upstream_keepalive_init_main_conf, /* init main configuration */
ngx_http_upstream_keepalive_create_conf, /* create server configuration */
NULL, /* merge server configuration */
@@ -152,52 +153,6 @@ ngx_module_t ngx_http_upstream_keepalive_module = {
};
static ngx_int_t
ngx_http_upstream_init_keepalive(ngx_conf_t *cf,
ngx_http_upstream_srv_conf_t *us)
{
ngx_uint_t i;
ngx_http_upstream_keepalive_srv_conf_t *kcf;
ngx_http_upstream_keepalive_cache_t *cached;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
"init keepalive");
kcf = ngx_http_conf_upstream_srv_conf(us,
ngx_http_upstream_keepalive_module);
ngx_conf_init_msec_value(kcf->time, 3600000);
ngx_conf_init_msec_value(kcf->timeout, 60000);
ngx_conf_init_uint_value(kcf->requests, 1000);
if (kcf->original_init_upstream(cf, us) != NGX_OK) {
return NGX_ERROR;
}
kcf->original_init_peer = us->peer.init;
us->peer.init = ngx_http_upstream_init_keepalive_peer;
/* allocate cache items and add to free queue */
cached = ngx_pcalloc(cf->pool,
sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached);
if (cached == NULL) {
return NGX_ERROR;
}
ngx_queue_init(&kcf->cache);
ngx_queue_init(&kcf->free);
for (i = 0; i < kcf->max_cached; i++) {
ngx_queue_insert_head(&kcf->free, &cached[i].queue);
cached[i].conf = kcf;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us)
@@ -550,30 +505,89 @@ ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf)
/*
* set by ngx_pcalloc():
*
* conf->original_init_upstream = NULL;
* conf->original_init_peer = NULL;
* conf->max_cached = 0;
* conf->local = 0;
*/
conf->time = NGX_CONF_UNSET_MSEC;
conf->timeout = NGX_CONF_UNSET_MSEC;
conf->requests = NGX_CONF_UNSET_UINT;
conf->max_cached = NGX_CONF_UNSET_UINT;
return conf;
}
static char *
ngx_http_upstream_keepalive_init_main_conf(ngx_conf_t *cf, void *conf)
{
ngx_uint_t i, j;
ngx_http_upstream_srv_conf_t **uscfp;
ngx_http_upstream_main_conf_t *umcf;
ngx_http_upstream_keepalive_cache_t *cached;
ngx_http_upstream_keepalive_srv_conf_t *kcf;
umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
uscfp = umcf->upstreams.elts;
for (i = 0; i < umcf->upstreams.nelts; i++) {
/* skip implicit upstreams */
if (uscfp[i]->srv_conf == NULL) {
continue;
}
kcf = ngx_http_conf_upstream_srv_conf(uscfp[i],
ngx_http_upstream_keepalive_module);
if (kcf->max_cached == 0) {
continue;
}
ngx_conf_init_msec_value(kcf->time, 3600000);
ngx_conf_init_msec_value(kcf->timeout, 60000);
ngx_conf_init_uint_value(kcf->requests, 1000);
if (kcf->max_cached == NGX_CONF_UNSET_UINT) {
kcf->local = 1;
kcf->max_cached = 32;
}
kcf->original_init_peer = uscfp[i]->peer.init;
uscfp[i]->peer.init = ngx_http_upstream_init_keepalive_peer;
/* allocate cache items and add to free queue */
cached = ngx_pcalloc(cf->pool,
sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached);
if (cached == NULL) {
return NGX_CONF_ERROR;
}
ngx_queue_init(&kcf->cache);
ngx_queue_init(&kcf->free);
for (j = 0; j < kcf->max_cached; j++) {
ngx_queue_insert_head(&kcf->free, &cached[j].queue);
cached[j].conf = kcf;
}
}
return NGX_CONF_OK;
}
static char *
ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_upstream_srv_conf_t *uscf;
ngx_http_upstream_keepalive_srv_conf_t *kcf = conf;
ngx_int_t n;
ngx_str_t *value;
if (kcf->max_cached) {
if (kcf->max_cached != NGX_CONF_UNSET_UINT) {
return "is duplicate";
}
@@ -583,7 +597,7 @@ ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
n = ngx_atoi(value[1].data, value[1].len);
if (n == NGX_ERROR || n == 0) {
if (n == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%V\" in \"%V\" directive",
&value[1], &cmd->name);
@@ -603,15 +617,5 @@ ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
}
/* init upstream handler */
uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
kcf->original_init_upstream = uscf->peer.init_upstream
? uscf->peer.init_upstream
: ngx_http_upstream_init_round_robin;
uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;
return NGX_CONF_OK;
}