Skip to content

Added support for refresh_in time and optional debug log #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ An nginx module to resolve domain names inside upstreams and keep them up to dat

By default, servers defined in nginx upstreams are only resolved when nginx starts. This module provides an additional `resolve` parameter for `server` definitions that can be used to asynchronously resolve upstream domain names. This keeps the upstream definition up to date according to the DNS TTL of each domain names. This can be useful if you want to use upstreams for dynamic types of domain names that may frequently change IP addresses.

In addition to `resolve`, it allow optional `refresh_interval` to modify default value of 1000 (1s).

This module also allows nginx to start if an upstream contains a defunct domain name that no longer resolves. By default, nginx will fail to start if an upstream server contains an unresolvable domain name. With this module, nginx is still allowed to start with invalid domain names, but an error will be logged and the unresolvable domain names will be marked as down.

## Installation
Expand All @@ -24,7 +26,7 @@ http {
resolver 8.8.8.8;

upstream example {
server example.com resolve;
server example.com resolve refresh_interval=1000;
}
}
```
Expand All @@ -49,6 +51,7 @@ The following parameters can be used (see nginx's [server documentation](http://
`backup`
`down`
`resolve`
`refresh_interval`

# Compatibility

Expand Down
46 changes: 31 additions & 15 deletions ngx_http_upstream_dynamic_servers.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ typedef struct {
ngx_str_t host;
in_port_t port;
ngx_event_t timer;
ngx_uint_t refresh_in;
} ngx_http_upstream_dynamic_server_conf_t;

typedef struct {
Expand Down Expand Up @@ -96,6 +97,8 @@ static char * ngx_http_upstream_dynamic_server_directive(ngx_conf_t *cf, ngx_com
ngx_int_t weight, max_fails;
ngx_uint_t i;
ngx_http_upstream_server_t *us;



#if nginx_version <= 1007002
if (uscf->servers == NULL) {
Expand Down Expand Up @@ -193,11 +196,11 @@ static char * ngx_http_upstream_dynamic_server_directive(ngx_conf_t *cf, ngx_com
}

// BEGIN CUSTOMIZATION: differs from default "server" implementation
if (ngx_strcmp(value[i].data, "resolve") == 0) {
if (ngx_strcmp(value[i].data, "resolve") == 0){
// Determine if the server given is an IP address or a hostname by running
// through ngx_parse_url with no_resolve enabled. Only if a hostname is given
// will we add this to the list of dynamic servers that we will resolve again.

ngx_memzero(&u, sizeof(ngx_url_t));
u.url = value[1];
u.default_port = 80;
Expand All @@ -215,10 +218,19 @@ static char * ngx_http_upstream_dynamic_server_directive(ngx_conf_t *cf, ngx_com

dynamic_server->host = u.host;
dynamic_server->port = (in_port_t) (u.no_port ? u.default_port : u.port);
dynamic_server->refresh_in = 1000;

}

continue;
}

// add support for refresh_interval.this must be followed by resolve
if (dynamic_server && (ngx_strncmp(value[i].data, "refresh_interval=", 17) == 0)){
dynamic_server->refresh_in = ngx_atoi(&value[i].data[17], value[i].len - 17);
continue;
}

// END CUSTOMIZATION

goto invalid;
Expand Down Expand Up @@ -297,7 +309,7 @@ static void *ngx_http_upstream_dynamic_server_main_conf(ngx_conf_t *cf) {
}

udsmcf->resolver_timeout = NGX_CONF_UNSET_MSEC;

return udsmcf;
}

Expand Down Expand Up @@ -382,6 +394,7 @@ static void ngx_http_upstream_dynamic_server_resolve(ngx_event_t *ev) {
ctx->timeout = udsmcf->resolver_timeout;

ngx_log_debug(NGX_LOG_DEBUG_CORE, ev->log, 0, "upstream-dynamic-servers: Resolving '%V'", &ctx->name);

if (ngx_resolve_name(ctx) != NGX_OK) {
ngx_log_error(NGX_LOG_ALERT, ev->log, 0, "upstream-dynamic-servers: ngx_resolve_name failed for '%V'", &ctx->name);
ngx_add_timer(&dynamic_server->timer, 1000);
Expand All @@ -392,14 +405,15 @@ static void ngx_http_upstream_dynamic_server_resolve_handler(ngx_resolver_ctx_t
ngx_http_upstream_dynamic_server_main_conf_t *udsmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_upstream_dynamic_servers_module);
ngx_http_upstream_dynamic_server_conf_t *dynamic_server;
ngx_conf_t cf;
uint32_t hash;
ngx_resolver_node_t *rn;
ngx_pool_t *new_pool;
ngx_addr_t *addrs;
ngx_uint_t refresh_in = 1000;

dynamic_server = ctx->data;

ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: Finished resolving '%V'", &ctx->name);


if (ctx->state) {
ngx_log_error(NGX_LOG_ERR, ctx->resolver->log, 0, "upstream-dynamic-servers: '%V' could not be resolved (%i: %s)", &ctx->name, ctx->state, ngx_resolver_strerror(ctx->state));
Expand Down Expand Up @@ -452,6 +466,7 @@ static void ngx_http_upstream_dynamic_server_resolve_handler(ngx_resolver_ctx_t
}

ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: No DNS changes for '%V' - keeping existing upstream configuration", &ctx->name);

goto end;

reinit_upstream:
Expand All @@ -463,6 +478,7 @@ static void ngx_http_upstream_dynamic_server_resolve_handler(ngx_resolver_ctx_t
}

ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: DNS changes for '%V' detected - reinitializing upstream configuration", &ctx->name);


ngx_memzero(&cf, sizeof(ngx_conf_t));
cf.name = "dynamic_server_init_upstream";
Expand Down Expand Up @@ -510,6 +526,7 @@ static void ngx_http_upstream_dynamic_server_resolve_handler(ngx_resolver_ctx_t
addr->name.len = len;
addr->name.data = p;
ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: '%V' was resolved to '%V'", &ctx->name, &addr->name);

}

// If the domain failed to resolve, mark this server as down.
Expand All @@ -533,22 +550,21 @@ static void ngx_http_upstream_dynamic_server_resolve_handler(ngx_resolver_ctx_t
dynamic_server->pool = new_pool;

end:

if(dynamic_server) {
refresh_in = dynamic_server->refresh_in;
}
if (ctx->resolver->log->log_level & NGX_LOG_DEBUG_CORE) {
hash = ngx_crc32_short(ctx->name.data, ctx->name.len);
uint32_t hash = ngx_crc32_short(ctx->name.data, ctx->name.len);
rn = ngx_resolver_lookup_name(ctx->resolver, &ctx->name, hash);
uint32_t refresh_in;
if (rn != NULL && rn->ttl) {
refresh_in = (rn->valid - ngx_time()) * 1000;

if (!refresh_in || refresh_in < 1000) {
refresh_in = 1000;
int32_t rin = rn->valid - ngx_time();
if ( rin > 0) {
refresh_in = rin * 1000;
}
} else {
refresh_in = 1000;
}

ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: Refreshing DNS of '%V' in %ims", &ctx->name, refresh_in);
ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: Refreshing DNS of '%V' in %ims", &ctx->name, refresh_in);

}

ngx_resolve_name_done(ctx);
Expand All @@ -558,7 +574,7 @@ static void ngx_http_upstream_dynamic_server_resolve_handler(ngx_resolver_ctx_t
return;
}

ngx_add_timer(&dynamic_server->timer, 1000);
ngx_add_timer(&dynamic_server->timer, refresh_in);
}

// Copied from src/core/ngx_resolver.c (nginx version 1.7.7).
Expand Down