mirror of
https://github.com/nginx/nginx.git
synced 2025-06-12 13:42:55 +08:00

Previously, all upstream DNS entries would be immediately re-resolved on config reload. With a large number of upstreams, this creates a spike of DNS resolution requests. These spikes can overwhelm the DNS server or cause drops on the network. This patch retains the TTL of previous resolutions across reloads by copying each upstream's name's expiry time across configuration cycles. As a result, no additional resolutions are needed.
1008 lines
28 KiB
C
1008 lines
28 KiB
C
|
|
/*
|
|
* Copyright (C) Ruslan Ermilov
|
|
* Copyright (C) Nginx, Inc.
|
|
*/
|
|
|
|
|
|
#include <ngx_config.h>
|
|
#include <ngx_core.h>
|
|
#include <ngx_http.h>
|
|
|
|
|
|
static char *ngx_http_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd,
|
|
void *conf);
|
|
static ngx_int_t ngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone,
|
|
void *data);
|
|
static ngx_http_upstream_rr_peers_t *ngx_http_upstream_zone_copy_peers(
|
|
ngx_slab_pool_t *shpool, ngx_http_upstream_srv_conf_t *uscf,
|
|
ngx_http_upstream_srv_conf_t *ouscf);
|
|
static ngx_http_upstream_rr_peer_t *ngx_http_upstream_zone_copy_peer(
|
|
ngx_http_upstream_rr_peers_t *peers, ngx_http_upstream_rr_peer_t *src);
|
|
static ngx_int_t ngx_http_upstream_zone_preresolve(
|
|
ngx_http_upstream_rr_peer_t *resolve,
|
|
ngx_http_upstream_rr_peers_t *peers,
|
|
ngx_http_upstream_rr_peer_t *oresolve,
|
|
ngx_http_upstream_rr_peers_t *opeers);
|
|
static void ngx_http_upstream_zone_set_single(
|
|
ngx_http_upstream_srv_conf_t *uscf);
|
|
static void ngx_http_upstream_zone_remove_peer_locked(
|
|
ngx_http_upstream_rr_peers_t *peers, ngx_http_upstream_rr_peer_t *peer);
|
|
static ngx_int_t ngx_http_upstream_zone_init_worker(ngx_cycle_t *cycle);
|
|
static void ngx_http_upstream_zone_resolve_timer(ngx_event_t *event);
|
|
static void ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx);
|
|
|
|
|
|
static ngx_command_t ngx_http_upstream_zone_commands[] = {
|
|
|
|
{ ngx_string("zone"),
|
|
NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,
|
|
ngx_http_upstream_zone,
|
|
0,
|
|
0,
|
|
NULL },
|
|
|
|
ngx_null_command
|
|
};
|
|
|
|
|
|
static ngx_http_module_t ngx_http_upstream_zone_module_ctx = {
|
|
NULL, /* preconfiguration */
|
|
NULL, /* postconfiguration */
|
|
|
|
NULL, /* create main configuration */
|
|
NULL, /* init main configuration */
|
|
|
|
NULL, /* create server configuration */
|
|
NULL, /* merge server configuration */
|
|
|
|
NULL, /* create location configuration */
|
|
NULL /* merge location configuration */
|
|
};
|
|
|
|
|
|
ngx_module_t ngx_http_upstream_zone_module = {
|
|
NGX_MODULE_V1,
|
|
&ngx_http_upstream_zone_module_ctx, /* module context */
|
|
ngx_http_upstream_zone_commands, /* module directives */
|
|
NGX_HTTP_MODULE, /* module type */
|
|
NULL, /* init master */
|
|
NULL, /* init module */
|
|
ngx_http_upstream_zone_init_worker, /* init process */
|
|
NULL, /* init thread */
|
|
NULL, /* exit thread */
|
|
NULL, /* exit process */
|
|
NULL, /* exit master */
|
|
NGX_MODULE_V1_PADDING
|
|
};
|
|
|
|
|
|
static char *
|
|
ngx_http_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
|
{
|
|
ssize_t size;
|
|
ngx_str_t *value;
|
|
ngx_http_upstream_srv_conf_t *uscf;
|
|
ngx_http_upstream_main_conf_t *umcf;
|
|
|
|
uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
|
|
umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
|
|
|
|
value = cf->args->elts;
|
|
|
|
if (!value[1].len) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
"invalid zone name \"%V\"", &value[1]);
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
if (cf->args->nelts == 3) {
|
|
size = ngx_parse_size(&value[2]);
|
|
|
|
if (size == NGX_ERROR) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
"invalid zone size \"%V\"", &value[2]);
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
if (size < (ssize_t) (8 * ngx_pagesize)) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
"zone \"%V\" is too small", &value[1]);
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
} else {
|
|
size = 0;
|
|
}
|
|
|
|
uscf->shm_zone = ngx_shared_memory_add(cf, &value[1], size,
|
|
&ngx_http_upstream_module);
|
|
if (uscf->shm_zone == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
uscf->shm_zone->init = ngx_http_upstream_init_zone;
|
|
uscf->shm_zone->data = umcf;
|
|
|
|
uscf->shm_zone->noreuse = 1;
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data)
|
|
{
|
|
size_t len;
|
|
ngx_uint_t i, j;
|
|
ngx_slab_pool_t *shpool;
|
|
ngx_http_upstream_rr_peers_t *peers, **peersp;
|
|
ngx_http_upstream_srv_conf_t *uscf, *ouscf, **uscfp, **ouscfp;
|
|
ngx_http_upstream_main_conf_t *umcf, *oumcf;
|
|
|
|
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
|
|
umcf = shm_zone->data;
|
|
uscfp = umcf->upstreams.elts;
|
|
|
|
if (shm_zone->shm.exists) {
|
|
peers = shpool->data;
|
|
|
|
for (i = 0; i < umcf->upstreams.nelts; i++) {
|
|
uscf = uscfp[i];
|
|
|
|
if (uscf->shm_zone != shm_zone) {
|
|
continue;
|
|
}
|
|
|
|
uscf->peer.data = peers;
|
|
peers = peers->zone_next;
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
len = sizeof(" in upstream zone \"\"") + shm_zone->shm.name.len;
|
|
|
|
shpool->log_ctx = ngx_slab_alloc(shpool, len);
|
|
if (shpool->log_ctx == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
ngx_sprintf(shpool->log_ctx, " in upstream zone \"%V\"%Z",
|
|
&shm_zone->shm.name);
|
|
|
|
|
|
/* copy peers to shared memory */
|
|
|
|
peersp = (ngx_http_upstream_rr_peers_t **) (void *) &shpool->data;
|
|
oumcf = data;
|
|
|
|
for (i = 0; i < umcf->upstreams.nelts; i++) {
|
|
uscf = uscfp[i];
|
|
|
|
if (uscf->shm_zone != shm_zone) {
|
|
continue;
|
|
}
|
|
|
|
ouscf = NULL;
|
|
|
|
if (oumcf) {
|
|
ouscfp = oumcf->upstreams.elts;
|
|
|
|
for (j = 0; j < oumcf->upstreams.nelts; j++) {
|
|
|
|
if (ouscfp[j]->shm_zone == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (ouscfp[j]->shm_zone->shm.name.len != shm_zone->shm.name.len
|
|
|| ngx_memcmp(ouscfp[j]->shm_zone->shm.name.data,
|
|
shm_zone->shm.name.data,
|
|
shm_zone->shm.name.len)
|
|
!= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (ouscfp[j]->host.len == uscf->host.len
|
|
&& ngx_memcmp(ouscfp[j]->host.data, uscf->host.data,
|
|
uscf->host.len)
|
|
== 0)
|
|
{
|
|
ouscf = ouscfp[j];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
peers = ngx_http_upstream_zone_copy_peers(shpool, uscf, ouscf);
|
|
if (peers == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
*peersp = peers;
|
|
peersp = &peers->zone_next;
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static ngx_http_upstream_rr_peers_t *
|
|
ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool,
|
|
ngx_http_upstream_srv_conf_t *uscf, ngx_http_upstream_srv_conf_t *ouscf)
|
|
{
|
|
ngx_str_t *name;
|
|
ngx_uint_t *config;
|
|
ngx_http_upstream_rr_peer_t *peer, **peerp;
|
|
ngx_http_upstream_rr_peers_t *peers, *opeers, *backup;
|
|
|
|
opeers = (ouscf ? ouscf->peer.data : NULL);
|
|
|
|
config = ngx_slab_calloc(shpool, sizeof(ngx_uint_t));
|
|
if (config == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
peers = ngx_slab_alloc(shpool, sizeof(ngx_http_upstream_rr_peers_t));
|
|
if (peers == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ngx_memcpy(peers, uscf->peer.data, sizeof(ngx_http_upstream_rr_peers_t));
|
|
|
|
name = ngx_slab_alloc(shpool, sizeof(ngx_str_t));
|
|
if (name == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
name->data = ngx_slab_alloc(shpool, peers->name->len);
|
|
if (name->data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ngx_memcpy(name->data, peers->name->data, peers->name->len);
|
|
name->len = peers->name->len;
|
|
|
|
peers->name = name;
|
|
|
|
peers->shpool = shpool;
|
|
peers->config = config;
|
|
|
|
for (peerp = &peers->peer; *peerp; peerp = &peer->next) {
|
|
/* pool is unlocked */
|
|
peer = ngx_http_upstream_zone_copy_peer(peers, *peerp);
|
|
if (peer == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
*peerp = peer;
|
|
(*peers->config)++;
|
|
}
|
|
|
|
for (peerp = &peers->resolve; *peerp; peerp = &peer->next) {
|
|
peer = ngx_http_upstream_zone_copy_peer(peers, *peerp);
|
|
if (peer == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
*peerp = peer;
|
|
(*peers->config)++;
|
|
}
|
|
|
|
if (opeers) {
|
|
|
|
if (ngx_http_upstream_zone_preresolve(peers->resolve, peers,
|
|
opeers->resolve, opeers)
|
|
!= NGX_OK)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (peers->next == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
backup = ngx_slab_alloc(shpool, sizeof(ngx_http_upstream_rr_peers_t));
|
|
if (backup == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ngx_memcpy(backup, peers->next, sizeof(ngx_http_upstream_rr_peers_t));
|
|
|
|
backup->name = name;
|
|
|
|
backup->shpool = shpool;
|
|
backup->config = config;
|
|
|
|
for (peerp = &backup->peer; *peerp; peerp = &peer->next) {
|
|
/* pool is unlocked */
|
|
peer = ngx_http_upstream_zone_copy_peer(backup, *peerp);
|
|
if (peer == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
*peerp = peer;
|
|
(*backup->config)++;
|
|
}
|
|
|
|
for (peerp = &backup->resolve; *peerp; peerp = &peer->next) {
|
|
peer = ngx_http_upstream_zone_copy_peer(backup, *peerp);
|
|
if (peer == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
*peerp = peer;
|
|
(*backup->config)++;
|
|
}
|
|
|
|
peers->next = backup;
|
|
|
|
if (opeers && opeers->next) {
|
|
|
|
if (ngx_http_upstream_zone_preresolve(peers->resolve, backup,
|
|
opeers->resolve, opeers->next)
|
|
!= NGX_OK)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (ngx_http_upstream_zone_preresolve(backup->resolve, backup,
|
|
opeers->next->resolve,
|
|
opeers->next)
|
|
!= NGX_OK)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
uscf->peer.data = peers;
|
|
|
|
ngx_http_upstream_zone_set_single(uscf);
|
|
|
|
return peers;
|
|
}
|
|
|
|
|
|
static ngx_http_upstream_rr_peer_t *
|
|
ngx_http_upstream_zone_copy_peer(ngx_http_upstream_rr_peers_t *peers,
|
|
ngx_http_upstream_rr_peer_t *src)
|
|
{
|
|
ngx_slab_pool_t *pool;
|
|
ngx_http_upstream_rr_peer_t *dst;
|
|
|
|
pool = peers->shpool;
|
|
|
|
dst = ngx_slab_calloc_locked(pool, sizeof(ngx_http_upstream_rr_peer_t));
|
|
if (dst == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (src) {
|
|
ngx_memcpy(dst, src, sizeof(ngx_http_upstream_rr_peer_t));
|
|
dst->sockaddr = NULL;
|
|
dst->name.data = NULL;
|
|
dst->server.data = NULL;
|
|
dst->host = NULL;
|
|
}
|
|
|
|
dst->sockaddr = ngx_slab_calloc_locked(pool, sizeof(ngx_sockaddr_t));
|
|
if (dst->sockaddr == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
dst->name.data = ngx_slab_calloc_locked(pool, NGX_SOCKADDR_STRLEN);
|
|
if (dst->name.data == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
if (src) {
|
|
ngx_memcpy(dst->sockaddr, src->sockaddr, src->socklen);
|
|
ngx_memcpy(dst->name.data, src->name.data, src->name.len);
|
|
|
|
dst->server.data = ngx_slab_alloc_locked(pool, src->server.len);
|
|
if (dst->server.data == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
ngx_memcpy(dst->server.data, src->server.data, src->server.len);
|
|
|
|
if (src->host) {
|
|
dst->host = ngx_slab_calloc_locked(pool,
|
|
sizeof(ngx_http_upstream_host_t));
|
|
if (dst->host == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
dst->host->name.data = ngx_slab_alloc_locked(pool,
|
|
src->host->name.len);
|
|
if (dst->host->name.data == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
dst->host->peers = peers;
|
|
dst->host->peer = dst;
|
|
|
|
dst->host->name.len = src->host->name.len;
|
|
ngx_memcpy(dst->host->name.data, src->host->name.data,
|
|
src->host->name.len);
|
|
|
|
if (src->host->service.len) {
|
|
dst->host->service.data = ngx_slab_alloc_locked(pool,
|
|
src->host->service.len);
|
|
if (dst->host->service.data == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
dst->host->service.len = src->host->service.len;
|
|
ngx_memcpy(dst->host->service.data, src->host->service.data,
|
|
src->host->service.len);
|
|
}
|
|
}
|
|
}
|
|
|
|
return dst;
|
|
|
|
failed:
|
|
|
|
if (dst->host) {
|
|
if (dst->host->name.data) {
|
|
ngx_slab_free_locked(pool, dst->host->name.data);
|
|
}
|
|
|
|
ngx_slab_free_locked(pool, dst->host);
|
|
}
|
|
|
|
if (dst->server.data) {
|
|
ngx_slab_free_locked(pool, dst->server.data);
|
|
}
|
|
|
|
if (dst->name.data) {
|
|
ngx_slab_free_locked(pool, dst->name.data);
|
|
}
|
|
|
|
if (dst->sockaddr) {
|
|
ngx_slab_free_locked(pool, dst->sockaddr);
|
|
}
|
|
|
|
ngx_slab_free_locked(pool, dst);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_http_upstream_zone_preresolve(ngx_http_upstream_rr_peer_t *resolve,
|
|
ngx_http_upstream_rr_peers_t *peers,
|
|
ngx_http_upstream_rr_peer_t *oresolve,
|
|
ngx_http_upstream_rr_peers_t *opeers)
|
|
{
|
|
in_port_t port;
|
|
ngx_str_t *server;
|
|
ngx_http_upstream_host_t *host;
|
|
ngx_http_upstream_rr_peer_t *peer, *template, *opeer, **peerp;
|
|
|
|
if (resolve == NULL || oresolve == NULL) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
for (peerp = &peers->peer; *peerp; peerp = &(*peerp)->next) {
|
|
/* void */
|
|
}
|
|
|
|
ngx_http_upstream_rr_peers_rlock(opeers);
|
|
|
|
for (template = resolve; template; template = template->next) {
|
|
for (opeer = oresolve; opeer; opeer = opeer->next) {
|
|
|
|
if (opeer->host->name.len != template->host->name.len
|
|
|| ngx_memcmp(opeer->host->name.data,
|
|
template->host->name.data,
|
|
template->host->name.len)
|
|
!= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (opeer->host->service.len != template->host->service.len
|
|
|| ngx_memcmp(opeer->host->service.data,
|
|
template->host->service.data,
|
|
template->host->service.len)
|
|
!= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
host = opeer->host;
|
|
|
|
for (opeer = opeers->peer; opeer; opeer = opeer->next) {
|
|
|
|
if (opeer->host != host) {
|
|
continue;
|
|
}
|
|
|
|
peer = ngx_http_upstream_zone_copy_peer(peers, NULL);
|
|
if (peer == NULL) {
|
|
ngx_http_upstream_rr_peers_unlock(opeers);
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
ngx_memcpy(peer->sockaddr, opeer->sockaddr, opeer->socklen);
|
|
|
|
if (template->host->service.len == 0) {
|
|
port = ngx_inet_get_port(template->sockaddr);
|
|
ngx_inet_set_port(peer->sockaddr, port);
|
|
}
|
|
|
|
peer->socklen = opeer->socklen;
|
|
|
|
peer->name.len = ngx_sock_ntop(peer->sockaddr, peer->socklen,
|
|
peer->name.data,
|
|
NGX_SOCKADDR_STRLEN, 1);
|
|
|
|
peer->host = template->host;
|
|
|
|
template->host->valid = host->valid;
|
|
|
|
server = template->host->service.len ? &opeer->server
|
|
: &template->server;
|
|
|
|
peer->server.data = ngx_slab_alloc(peers->shpool, server->len);
|
|
if (peer->server.data == NULL) {
|
|
ngx_http_upstream_rr_peers_unlock(opeers);
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
ngx_memcpy(peer->server.data, server->data, server->len);
|
|
peer->server.len = server->len;
|
|
|
|
if (host->service.len == 0) {
|
|
peer->weight = template->weight;
|
|
|
|
} else {
|
|
peer->weight = (template->weight != 1 ? template->weight
|
|
: opeer->weight);
|
|
}
|
|
|
|
peer->effective_weight = peer->weight;
|
|
peer->max_conns = template->max_conns;
|
|
peer->max_fails = template->max_fails;
|
|
peer->fail_timeout = template->fail_timeout;
|
|
peer->down = template->down;
|
|
|
|
(*peers->config)++;
|
|
|
|
*peerp = peer;
|
|
peerp = &peer->next;
|
|
|
|
peers->number++;
|
|
peers->tries += (peer->down == 0);
|
|
peers->total_weight += peer->weight;
|
|
peers->weighted = (peers->total_weight != peers->number);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
ngx_http_upstream_rr_peers_unlock(opeers);
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_http_upstream_zone_set_single(ngx_http_upstream_srv_conf_t *uscf)
|
|
{
|
|
ngx_http_upstream_rr_peers_t *peers;
|
|
|
|
peers = uscf->peer.data;
|
|
|
|
if (peers->number == 1
|
|
&& (peers->next == NULL || peers->next->number == 0))
|
|
{
|
|
peers->single = 1;
|
|
|
|
} else {
|
|
peers->single = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_http_upstream_zone_remove_peer_locked(ngx_http_upstream_rr_peers_t *peers,
|
|
ngx_http_upstream_rr_peer_t *peer)
|
|
{
|
|
peers->total_weight -= peer->weight;
|
|
peers->number--;
|
|
peers->tries -= (peer->down == 0);
|
|
(*peers->config)++;
|
|
peers->weighted = (peers->total_weight != peers->number);
|
|
|
|
ngx_http_upstream_rr_peer_free(peers, peer);
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_http_upstream_zone_init_worker(ngx_cycle_t *cycle)
|
|
{
|
|
time_t now;
|
|
ngx_msec_t timer;
|
|
ngx_uint_t i;
|
|
ngx_event_t *event;
|
|
ngx_http_upstream_rr_peer_t *peer;
|
|
ngx_http_upstream_rr_peers_t *peers;
|
|
ngx_http_upstream_srv_conf_t *uscf, **uscfp;
|
|
ngx_http_upstream_main_conf_t *umcf;
|
|
|
|
if (ngx_process != NGX_PROCESS_WORKER
|
|
&& ngx_process != NGX_PROCESS_SINGLE)
|
|
{
|
|
return NGX_OK;
|
|
}
|
|
|
|
now = ngx_time();
|
|
umcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_upstream_module);
|
|
|
|
if (umcf == NULL) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
uscfp = umcf->upstreams.elts;
|
|
|
|
for (i = 0; i < umcf->upstreams.nelts; i++) {
|
|
|
|
uscf = uscfp[i];
|
|
|
|
if (uscf->shm_zone == NULL) {
|
|
continue;
|
|
}
|
|
|
|
peers = uscf->peer.data;
|
|
|
|
do {
|
|
ngx_http_upstream_rr_peers_wlock(peers);
|
|
|
|
for (peer = peers->resolve; peer; peer = peer->next) {
|
|
|
|
if (peer->host->worker != ngx_worker) {
|
|
continue;
|
|
}
|
|
|
|
event = &peer->host->event;
|
|
ngx_memzero(event, sizeof(ngx_event_t));
|
|
|
|
event->data = uscf;
|
|
event->handler = ngx_http_upstream_zone_resolve_timer;
|
|
event->log = cycle->log;
|
|
event->cancelable = 1;
|
|
|
|
timer = (peer->host->valid > now)
|
|
? (ngx_msec_t) 1000 * (peer->host->valid - now) : 1;
|
|
|
|
ngx_add_timer(event, timer);
|
|
}
|
|
|
|
ngx_http_upstream_rr_peers_unlock(peers);
|
|
|
|
peers = peers->next;
|
|
|
|
} while (peers);
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_http_upstream_zone_resolve_timer(ngx_event_t *event)
|
|
{
|
|
ngx_resolver_ctx_t *ctx;
|
|
ngx_http_upstream_host_t *host;
|
|
ngx_http_upstream_srv_conf_t *uscf;
|
|
|
|
host = (ngx_http_upstream_host_t *) event;
|
|
uscf = event->data;
|
|
|
|
ctx = ngx_resolve_start(uscf->resolver, NULL);
|
|
if (ctx == NULL) {
|
|
goto retry;
|
|
}
|
|
|
|
if (ctx == NGX_NO_RESOLVER) {
|
|
ngx_log_error(NGX_LOG_ERR, event->log, 0,
|
|
"no resolver defined to resolve %V", &host->name);
|
|
return;
|
|
}
|
|
|
|
ctx->name = host->name;
|
|
ctx->handler = ngx_http_upstream_zone_resolve_handler;
|
|
ctx->data = host;
|
|
ctx->timeout = uscf->resolver_timeout;
|
|
ctx->service = host->service;
|
|
ctx->cancelable = 1;
|
|
|
|
if (ngx_resolve_name(ctx) == NGX_OK) {
|
|
return;
|
|
}
|
|
|
|
retry:
|
|
|
|
ngx_add_timer(event, ngx_max(uscf->resolver_timeout, 1000));
|
|
}
|
|
|
|
|
|
#define ngx_http_upstream_zone_addr_marked(addr) \
|
|
((uintptr_t) (addr)->sockaddr & 1)
|
|
|
|
#define ngx_http_upstream_zone_mark_addr(addr) \
|
|
(addr)->sockaddr = (struct sockaddr *) ((uintptr_t) (addr)->sockaddr | 1)
|
|
|
|
#define ngx_http_upstream_zone_unmark_addr(addr) \
|
|
(addr)->sockaddr = \
|
|
(struct sockaddr *) ((uintptr_t) (addr)->sockaddr & ~((uintptr_t) 1))
|
|
|
|
static void
|
|
ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
|
|
{
|
|
time_t now;
|
|
u_short min_priority;
|
|
in_port_t port;
|
|
ngx_str_t *server;
|
|
ngx_msec_t timer;
|
|
ngx_uint_t i, j, backup, addr_backup;
|
|
ngx_event_t *event;
|
|
ngx_resolver_addr_t *addr;
|
|
ngx_resolver_srv_name_t *srv;
|
|
ngx_http_upstream_host_t *host;
|
|
ngx_http_upstream_rr_peer_t *peer, *template, **peerp;
|
|
ngx_http_upstream_rr_peers_t *peers;
|
|
ngx_http_upstream_srv_conf_t *uscf;
|
|
|
|
host = ctx->data;
|
|
event = &host->event;
|
|
uscf = event->data;
|
|
peers = host->peers;
|
|
template = host->peer;
|
|
|
|
ngx_http_upstream_rr_peers_wlock(peers);
|
|
|
|
now = ngx_time();
|
|
|
|
for (i = 0; i < ctx->nsrvs; i++) {
|
|
srv = &ctx->srvs[i];
|
|
|
|
if (srv->state) {
|
|
ngx_log_error(NGX_LOG_ERR, event->log, 0,
|
|
"%V could not be resolved (%i: %s) "
|
|
"while resolving service %V of %V",
|
|
&srv->name, srv->state,
|
|
ngx_resolver_strerror(srv->state), &ctx->service,
|
|
&ctx->name);
|
|
}
|
|
}
|
|
|
|
if (ctx->state) {
|
|
if (ctx->service.len) {
|
|
ngx_log_error(NGX_LOG_ERR, event->log, 0,
|
|
"service %V of %V could not be resolved (%i: %s)",
|
|
&ctx->service, &ctx->name, ctx->state,
|
|
ngx_resolver_strerror(ctx->state));
|
|
|
|
} else {
|
|
ngx_log_error(NGX_LOG_ERR, event->log, 0,
|
|
"%V could not be resolved (%i: %s)",
|
|
&ctx->name, ctx->state,
|
|
ngx_resolver_strerror(ctx->state));
|
|
}
|
|
|
|
if (ctx->state != NGX_RESOLVE_NXDOMAIN) {
|
|
ngx_http_upstream_rr_peers_unlock(peers);
|
|
|
|
ngx_resolve_name_done(ctx);
|
|
|
|
ngx_add_timer(event, ngx_max(uscf->resolver_timeout, 1000));
|
|
return;
|
|
}
|
|
|
|
/* NGX_RESOLVE_NXDOMAIN */
|
|
|
|
ctx->naddrs = 0;
|
|
}
|
|
|
|
backup = 0;
|
|
min_priority = 65535;
|
|
|
|
for (i = 0; i < ctx->naddrs; i++) {
|
|
min_priority = ngx_min(ctx->addrs[i].priority, min_priority);
|
|
}
|
|
|
|
#if (NGX_DEBUG)
|
|
{
|
|
u_char text[NGX_SOCKADDR_STRLEN];
|
|
size_t len;
|
|
|
|
for (i = 0; i < ctx->naddrs; i++) {
|
|
len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,
|
|
text, NGX_SOCKADDR_STRLEN, 1);
|
|
|
|
ngx_log_debug7(NGX_LOG_DEBUG_HTTP, event->log, 0,
|
|
"name %V was resolved to %*s "
|
|
"s:\"%V\" n:\"%V\" w:%d %s",
|
|
&host->name, len, text, &host->service,
|
|
&ctx->addrs[i].name, ctx->addrs[i].weight,
|
|
ctx->addrs[i].priority != min_priority ? "backup" : "");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
again:
|
|
|
|
for (peerp = &peers->peer; *peerp; /* void */ ) {
|
|
peer = *peerp;
|
|
|
|
if (peer->host != host) {
|
|
goto next;
|
|
}
|
|
|
|
for (j = 0; j < ctx->naddrs; j++) {
|
|
|
|
addr = &ctx->addrs[j];
|
|
|
|
addr_backup = (addr->priority != min_priority);
|
|
if (addr_backup != backup) {
|
|
continue;
|
|
}
|
|
|
|
if (ngx_http_upstream_zone_addr_marked(addr)) {
|
|
continue;
|
|
}
|
|
|
|
if (ngx_cmp_sockaddr(peer->sockaddr, peer->socklen,
|
|
addr->sockaddr, addr->socklen,
|
|
host->service.len != 0)
|
|
!= NGX_OK)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (host->service.len) {
|
|
if (addr->name.len != peer->server.len
|
|
|| ngx_strncmp(addr->name.data, peer->server.data,
|
|
addr->name.len))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (template->weight == 1 && addr->weight != peer->weight) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ngx_http_upstream_zone_mark_addr(addr);
|
|
|
|
goto next;
|
|
}
|
|
|
|
*peerp = peer->next;
|
|
ngx_http_upstream_zone_remove_peer_locked(peers, peer);
|
|
|
|
ngx_http_upstream_zone_set_single(uscf);
|
|
|
|
continue;
|
|
|
|
next:
|
|
|
|
peerp = &peer->next;
|
|
}
|
|
|
|
for (i = 0; i < ctx->naddrs; i++) {
|
|
|
|
addr = &ctx->addrs[i];
|
|
|
|
addr_backup = (addr->priority != min_priority);
|
|
if (addr_backup != backup) {
|
|
continue;
|
|
}
|
|
|
|
if (ngx_http_upstream_zone_addr_marked(addr)) {
|
|
ngx_http_upstream_zone_unmark_addr(addr);
|
|
continue;
|
|
}
|
|
|
|
ngx_shmtx_lock(&peers->shpool->mutex);
|
|
peer = ngx_http_upstream_zone_copy_peer(peers, NULL);
|
|
ngx_shmtx_unlock(&peers->shpool->mutex);
|
|
|
|
if (peer == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, event->log, 0,
|
|
"cannot add new server to upstream \"%V\", "
|
|
"memory exhausted", peers->name);
|
|
goto done;
|
|
}
|
|
|
|
ngx_memcpy(peer->sockaddr, addr->sockaddr, addr->socklen);
|
|
|
|
if (host->service.len == 0) {
|
|
port = ngx_inet_get_port(template->sockaddr);
|
|
ngx_inet_set_port(peer->sockaddr, port);
|
|
}
|
|
|
|
peer->socklen = addr->socklen;
|
|
|
|
peer->name.len = ngx_sock_ntop(peer->sockaddr, peer->socklen,
|
|
peer->name.data, NGX_SOCKADDR_STRLEN, 1);
|
|
|
|
peer->host = template->host;
|
|
|
|
server = host->service.len ? &addr->name : &template->server;
|
|
|
|
peer->server.data = ngx_slab_alloc(peers->shpool, server->len);
|
|
if (peer->server.data == NULL) {
|
|
ngx_http_upstream_rr_peer_free(peers, peer);
|
|
|
|
ngx_log_error(NGX_LOG_ERR, event->log, 0,
|
|
"cannot add new server to upstream \"%V\", "
|
|
"memory exhausted", peers->name);
|
|
goto done;
|
|
}
|
|
|
|
peer->server.len = server->len;
|
|
ngx_memcpy(peer->server.data, server->data, server->len);
|
|
|
|
if (host->service.len == 0) {
|
|
peer->weight = template->weight;
|
|
|
|
} else {
|
|
peer->weight = (template->weight != 1 ? template->weight
|
|
: addr->weight);
|
|
}
|
|
|
|
peer->effective_weight = peer->weight;
|
|
peer->max_conns = template->max_conns;
|
|
peer->max_fails = template->max_fails;
|
|
peer->fail_timeout = template->fail_timeout;
|
|
peer->down = template->down;
|
|
|
|
*peerp = peer;
|
|
peerp = &peer->next;
|
|
|
|
peers->number++;
|
|
peers->tries += (peer->down == 0);
|
|
peers->total_weight += peer->weight;
|
|
peers->weighted = (peers->total_weight != peers->number);
|
|
(*peers->config)++;
|
|
|
|
ngx_http_upstream_zone_set_single(uscf);
|
|
}
|
|
|
|
if (host->service.len && peers->next) {
|
|
ngx_http_upstream_rr_peers_unlock(peers);
|
|
|
|
peers = peers->next;
|
|
backup = 1;
|
|
|
|
ngx_http_upstream_rr_peers_wlock(peers);
|
|
|
|
goto again;
|
|
}
|
|
|
|
done:
|
|
|
|
host->valid = ctx->valid;
|
|
|
|
ngx_http_upstream_rr_peers_unlock(peers);
|
|
|
|
while (++i < ctx->naddrs) {
|
|
ngx_http_upstream_zone_unmark_addr(&ctx->addrs[i]);
|
|
}
|
|
|
|
timer = (ngx_msec_t) 1000 * (ctx->valid > now ? ctx->valid - now + 1 : 1);
|
|
|
|
ngx_resolve_name_done(ctx);
|
|
|
|
ngx_add_timer(event, timer);
|
|
}
|