[nginx] Upstream: construct upstream peers from DNS SRV records.

noreply at nginx.com noreply at nginx.com
Thu Nov 7 15:58:03 UTC 2024


details:   https://github.com/nginx/nginx/commit/9fe119b431c957824d7bed75fce47dfbda74ca33
branches:  master
commit:    9fe119b431c957824d7bed75fce47dfbda74ca33
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Thu, 17 Mar 2016 18:42:31 +0300
description:
Upstream: construct upstream peers from DNS SRV records.


---
 src/http/modules/ngx_http_upstream_zone_module.c | 183 ++++++++++++++++++----
 src/http/ngx_http_upstream.c                     |  38 +++++
 src/http/ngx_http_upstream.h                     |   3 +-
 src/http/ngx_http_upstream_round_robin.c         |  12 +-
 src/http/ngx_http_upstream_round_robin.h         |   3 +-
 src/stream/ngx_stream_upstream.c                 |  45 +++++-
 src/stream/ngx_stream_upstream.h                 |   4 +-
 src/stream/ngx_stream_upstream_round_robin.c     |  12 +-
 src/stream/ngx_stream_upstream_round_robin.h     |   3 +-
 src/stream/ngx_stream_upstream_zone_module.c     | 184 +++++++++++++++++++----
 10 files changed, 420 insertions(+), 67 deletions(-)

diff --git a/src/http/modules/ngx_http_upstream_zone_module.c b/src/http/modules/ngx_http_upstream_zone_module.c
index 355df39ab..e3ede9268 100644
--- a/src/http/modules/ngx_http_upstream_zone_module.c
+++ b/src/http/modules/ngx_http_upstream_zone_module.c
@@ -359,6 +359,18 @@ ngx_http_upstream_zone_copy_peer(ngx_http_upstream_rr_peers_t *peers,
             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);
+            }
         }
     }
 
@@ -367,6 +379,10 @@ ngx_http_upstream_zone_copy_peer(ngx_http_upstream_rr_peers_t *peers,
 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);
     }
 
@@ -510,6 +526,7 @@ ngx_http_upstream_zone_resolve_timer(ngx_event_t *event)
     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) {
@@ -522,15 +539,28 @@ retry:
 }
 
 
+#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;
+    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;
@@ -546,11 +576,32 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
 
     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) {
-        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->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);
@@ -566,6 +617,13 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
         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];
@@ -573,14 +631,20 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
 
     for (i = 0; i < ctx->naddrs; i++) {
         len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,
-                            text, NGX_SOCKADDR_STRLEN, 0);
+                            text, NGX_SOCKADDR_STRLEN, 1);
 
-        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, event->log, 0,
-                       "name %V was resolved to %*s", &host->name, len, text);
+        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;
 
@@ -592,14 +656,39 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
 
             addr = &ctx->addrs[j];
 
-            if (addr->name.len == 0
-                && ngx_cmp_sockaddr(peer->sockaddr, peer->socklen,
-                                    addr->sockaddr, addr->socklen, 0)
-                   == NGX_OK)
+            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)
             {
-                addr->name.len = 1;
-                goto next;
+                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;
@@ -618,8 +707,13 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
 
         addr = &ctx->addrs[i];
 
-        if (addr->name.len == 1) {
-            addr->name.len = 0;
+        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;
         }
 
@@ -631,21 +725,14 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
             ngx_log_error(NGX_LOG_ERR, event->log, 0,
                           "cannot add new server to upstream \"%V\", "
                           "memory exhausted", peers->name);
-            break;
+            goto done;
         }
 
         ngx_memcpy(peer->sockaddr, addr->sockaddr, addr->socklen);
 
-        port = ((struct sockaddr_in *) template->sockaddr)->sin_port;
-
-        switch (peer->sockaddr->sa_family) {
-#if (NGX_HAVE_INET6)
-        case AF_INET6:
-            ((struct sockaddr_in6 *) peer->sockaddr)->sin6_port = port;
-            break;
-#endif
-        default: /* AF_INET */
-            ((struct sockaddr_in *) peer->sockaddr)->sin_port = port;
+        if (host->service.len == 0) {
+            port = ngx_inet_get_port(template->sockaddr);
+            ngx_inet_set_port(peer->sockaddr, port);
         }
 
         peer->socklen = addr->socklen;
@@ -654,9 +741,30 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
                                        peer->name.data, NGX_SOCKADDR_STRLEN, 1);
 
         peer->host = template->host;
-        peer->server = template->server;
 
-        peer->weight = template->weight;
+        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;
@@ -675,8 +783,25 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
         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:
+
     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);
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index efe772439..d090d1618 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -6306,6 +6306,19 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
             resolve = 1;
             continue;
         }
+
+        if (ngx_strncmp(value[i].data, "service=", 8) == 0) {
+
+            us->service.len = value[i].len - 8;
+            us->service.data = &value[i].data[8];
+
+            if (us->service.len == 0) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "service is empty");
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
 #endif
 
         goto invalid;
@@ -6321,6 +6334,15 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
         /* resolve at run time */
         u.no_resolve = 1;
     }
+
+    if (us->service.len && !resolve) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "service upstream \"%V\" requires "
+                           "\"resolve\" parameter",
+                           &u.url);
+        return NGX_CONF_ERROR;
+    }
+
 #endif
 
     if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
@@ -6336,6 +6358,22 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 
 #if (NGX_HTTP_UPSTREAM_ZONE)
 
+    if (us->service.len && !u.no_port) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "service upstream \"%V\" may not have port",
+                           &us->name);
+
+        return NGX_CONF_ERROR;
+    }
+
+    if (us->service.len && u.naddrs) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "service upstream \"%V\" requires domain name",
+                           &us->name);
+
+        return NGX_CONF_ERROR;
+    }
+
     if (resolve && u.naddrs == 0) {
         ngx_addr_t  *addr;
 
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
index 0922021bd..57ee06f8d 100644
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -106,9 +106,10 @@ typedef struct {
 
 #if (NGX_HTTP_UPSTREAM_ZONE)
     ngx_str_t                        host;
+    ngx_str_t                        service;
 #endif
 
-    NGX_COMPAT_BEGIN(4)
+    NGX_COMPAT_BEGIN(2)
     NGX_COMPAT_END
 } ngx_http_upstream_server_t;
 
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
index cc1b6d1a2..5dbd4e626 100644
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -176,6 +176,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
                 }
 
                 peer[n].host->name = server[i].host;
+                peer[n].host->service = server[i].service;
 
                 peer[n].sockaddr = server[i].addrs[0].sockaddr;
                 peer[n].socklen = server[i].addrs[0].socklen;
@@ -245,7 +246,15 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
             }
         }
 
-        if (n + r == 0) {
+        if (n == 0
+#if (NGX_HTTP_UPSTREAM_ZONE)
+            && !resolve
+#endif
+        ) {
+            return NGX_OK;
+        }
+
+        if (n + r == 0 && !(us->flags & NGX_HTTP_UPSTREAM_BACKUP)) {
             return NGX_OK;
         }
 
@@ -293,6 +302,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
                 }
 
                 peer[n].host->name = server[i].host;
+                peer[n].host->service = server[i].service;
 
                 peer[n].sockaddr = server[i].addrs[0].sockaddr;
                 peer[n].socklen = server[i].addrs[0].socklen;
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
index 06437f405..084b0b886 100644
--- a/src/http/ngx_http_upstream_round_robin.h
+++ b/src/http/ngx_http_upstream_round_robin.h
@@ -24,6 +24,7 @@ typedef struct {
     ngx_event_t                     event;         /* must be first */
     ngx_uint_t                      worker;
     ngx_str_t                       name;
+    ngx_str_t                       service;
     ngx_http_upstream_rr_peers_t   *peers;
     ngx_http_upstream_rr_peer_t    *peer;
 } ngx_http_upstream_host_t;
@@ -150,7 +151,7 @@ ngx_http_upstream_rr_peer_free_locked(ngx_http_upstream_rr_peers_t *peers,
     ngx_slab_free_locked(peers->shpool, peer->sockaddr);
     ngx_slab_free_locked(peers->shpool, peer->name.data);
 
-    if (peer->server.data && (peer->host == NULL || peer->host->peer == peer)) {
+    if (peer->server.data) {
         ngx_slab_free_locked(peers->shpool, peer->server.data);
     }
 
diff --git a/src/stream/ngx_stream_upstream.c b/src/stream/ngx_stream_upstream.c
index a251cca00..be4f13016 100644
--- a/src/stream/ngx_stream_upstream.c
+++ b/src/stream/ngx_stream_upstream.c
@@ -523,6 +523,19 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
             resolve = 1;
             continue;
         }
+
+        if (ngx_strncmp(value[i].data, "service=", 8) == 0) {
+
+            us->service.len = value[i].len - 8;
+            us->service.data = &value[i].data[8];
+
+            if (us->service.len == 0) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "service is empty");
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
 #endif
 
         goto invalid;
@@ -537,6 +550,15 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
         /* resolve at run time */
         u.no_resolve = 1;
     }
+
+    if (us->service.len && !resolve) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "service upstream \"%V\" requires "
+                           "\"resolve\" parameter",
+                           &u.url);
+        return NGX_CONF_ERROR;
+    }
+
 #endif
 
     if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
@@ -548,7 +570,12 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
         return NGX_CONF_ERROR;
     }
 
-    if (u.no_port) {
+    if (u.no_port
+#if (NGX_STREAM_UPSTREAM_ZONE)
+        && us->service.len == 0
+#endif
+        )
+    {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "no port in upstream \"%V\"", &u.url);
         return NGX_CONF_ERROR;
@@ -558,6 +585,22 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 
 #if (NGX_STREAM_UPSTREAM_ZONE)
 
+    if (us->service.len && !u.no_port) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "service upstream \"%V\" may not have port",
+                           &us->name);
+
+        return NGX_CONF_ERROR;
+    }
+
+    if (us->service.len && u.naddrs) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "service upstream \"%V\" requires domain name",
+                           &us->name);
+
+        return NGX_CONF_ERROR;
+    }
+
     if (resolve && u.naddrs == 0) {
         ngx_addr_t  *addr;
 
diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h
index 686e9269c..c581aa0be 100644
--- a/src/stream/ngx_stream_upstream.h
+++ b/src/stream/ngx_stream_upstream.h
@@ -65,10 +65,8 @@ typedef struct {
 
 #if (NGX_STREAM_UPSTREAM_ZONE)
     ngx_str_t                          host;
+    ngx_str_t                          service;
 #endif
-
-    NGX_COMPAT_BEGIN(2)
-    NGX_COMPAT_END
 } ngx_stream_upstream_server_t;
 
 
diff --git a/src/stream/ngx_stream_upstream_round_robin.c b/src/stream/ngx_stream_upstream_round_robin.c
index a18171a39..e1903b3d2 100644
--- a/src/stream/ngx_stream_upstream_round_robin.c
+++ b/src/stream/ngx_stream_upstream_round_robin.c
@@ -183,6 +183,7 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf,
                 }
 
                 peer[n].host->name = server[i].host;
+                peer[n].host->service = server[i].service;
 
                 peer[n].sockaddr = server[i].addrs[0].sockaddr;
                 peer[n].socklen = server[i].addrs[0].socklen;
@@ -251,7 +252,15 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf,
             }
         }
 
-        if (n + r == 0) {
+        if (n == 0
+#if (NGX_STREAM_UPSTREAM_ZONE)
+            && !resolve
+#endif
+        ) {
+            return NGX_OK;
+        }
+
+        if (n + r == 0 && !(us->flags & NGX_STREAM_UPSTREAM_BACKUP)) {
             return NGX_OK;
         }
 
@@ -299,6 +308,7 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf,
                 }
 
                 peer[n].host->name = server[i].host;
+                peer[n].host->service = server[i].service;
 
                 peer[n].sockaddr = server[i].addrs[0].sockaddr;
                 peer[n].socklen = server[i].addrs[0].socklen;
diff --git a/src/stream/ngx_stream_upstream_round_robin.h b/src/stream/ngx_stream_upstream_round_robin.h
index 5002200c2..707a9889d 100644
--- a/src/stream/ngx_stream_upstream_round_robin.h
+++ b/src/stream/ngx_stream_upstream_round_robin.h
@@ -24,6 +24,7 @@ typedef struct {
     ngx_event_t                      event;         /* must be first */
     ngx_uint_t                       worker;
     ngx_str_t                        name;
+    ngx_str_t                        service;
     ngx_stream_upstream_rr_peers_t  *peers;
     ngx_stream_upstream_rr_peer_t   *peer;
 } ngx_stream_upstream_host_t;
@@ -148,7 +149,7 @@ ngx_stream_upstream_rr_peer_free_locked(ngx_stream_upstream_rr_peers_t *peers,
     ngx_slab_free_locked(peers->shpool, peer->sockaddr);
     ngx_slab_free_locked(peers->shpool, peer->name.data);
 
-    if (peer->server.data && (peer->host == NULL || peer->host->peer == peer)) {
+    if (peer->server.data) {
         ngx_slab_free_locked(peers->shpool, peer->server.data);
     }
 
diff --git a/src/stream/ngx_stream_upstream_zone_module.c b/src/stream/ngx_stream_upstream_zone_module.c
index d731baf31..0d6ab89f3 100644
--- a/src/stream/ngx_stream_upstream_zone_module.c
+++ b/src/stream/ngx_stream_upstream_zone_module.c
@@ -356,6 +356,18 @@ ngx_stream_upstream_zone_copy_peer(ngx_stream_upstream_rr_peers_t *peers,
             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);
+            }
         }
     }
 
@@ -364,6 +376,10 @@ ngx_stream_upstream_zone_copy_peer(ngx_stream_upstream_rr_peers_t *peers,
 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);
     }
 
@@ -508,6 +524,7 @@ ngx_stream_upstream_zone_resolve_timer(ngx_event_t *event)
     ctx->handler = ngx_stream_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) {
@@ -520,15 +537,28 @@ retry:
 }
 
 
+#define ngx_stream_upstream_zone_addr_marked(addr)                            \
+    ((uintptr_t) (addr)->sockaddr & 1)
+
+#define ngx_stream_upstream_zone_mark_addr(addr)                              \
+    (addr)->sockaddr = (struct sockaddr *) ((uintptr_t) (addr)->sockaddr | 1)
+
+#define ngx_stream_upstream_zone_unmark_addr(addr)                            \
+    (addr)->sockaddr =                                                        \
+        (struct sockaddr *) ((uintptr_t) (addr)->sockaddr & ~((uintptr_t) 1))
+
 static void
 ngx_stream_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;
+    ngx_uint_t                       i, j, backup, addr_backup;
     ngx_event_t                     *event;
     ngx_resolver_addr_t             *addr;
+    ngx_resolver_srv_name_t         *srv;
     ngx_stream_upstream_host_t      *host;
     ngx_stream_upstream_rr_peer_t   *peer, *template, **peerp;
     ngx_stream_upstream_rr_peers_t  *peers;
@@ -544,11 +574,32 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
 
     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) {
-        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->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_stream_upstream_rr_peers_unlock(peers);
@@ -564,6 +615,13 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
         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];
@@ -571,14 +629,20 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
 
     for (i = 0; i < ctx->naddrs; i++) {
         len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,
-                            text, NGX_SOCKADDR_STRLEN, 0);
+                            text, NGX_SOCKADDR_STRLEN, 1);
 
-        ngx_log_debug3(NGX_LOG_DEBUG_STREAM, event->log, 0,
-                       "name %V was resolved to %*s", &host->name, len, text);
+        ngx_log_debug7(NGX_LOG_DEBUG_STREAM, 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;
 
@@ -590,14 +654,39 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
 
             addr = &ctx->addrs[j];
 
-            if (addr->name.len == 0
-                && ngx_cmp_sockaddr(peer->sockaddr, peer->socklen,
-                                    addr->sockaddr, addr->socklen, 0)
-                   == NGX_OK)
+            addr_backup = (addr->priority != min_priority);
+            if (addr_backup != backup) {
+                continue;
+            }
+
+            if (ngx_stream_upstream_zone_addr_marked(addr)) {
+                continue;
+            }
+
+            if (ngx_cmp_sockaddr(peer->sockaddr, peer->socklen,
+                                 addr->sockaddr, addr->socklen,
+                                 host->service.len != 0)
+                != NGX_OK)
             {
-                addr->name.len = 1;
-                goto next;
+                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_stream_upstream_zone_mark_addr(addr);
+
+            goto next;
         }
 
         *peerp = peer->next;
@@ -616,33 +705,32 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
 
         addr = &ctx->addrs[i];
 
-        if (addr->name.len == 1) {
-            addr->name.len = 0;
+        addr_backup = (addr->priority != min_priority);
+        if (addr_backup != backup) {
+            continue;
+        }
+
+        if (ngx_stream_upstream_zone_addr_marked(addr)) {
+            ngx_stream_upstream_zone_unmark_addr(addr);
             continue;
         }
 
         ngx_shmtx_lock(&peers->shpool->mutex);
         peer = ngx_stream_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);
-            break;
+            goto done;
         }
 
         ngx_memcpy(peer->sockaddr, addr->sockaddr, addr->socklen);
 
-        port = ((struct sockaddr_in *) template->sockaddr)->sin_port;
-
-        switch (peer->sockaddr->sa_family) {
-#if (NGX_HAVE_INET6)
-        case AF_INET6:
-            ((struct sockaddr_in6 *) peer->sockaddr)->sin6_port = port;
-            break;
-#endif
-        default: /* AF_INET */
-            ((struct sockaddr_in *) peer->sockaddr)->sin_port = port;
+        if (host->service.len == 0) {
+            port = ngx_inet_get_port(template->sockaddr);
+            ngx_inet_set_port(peer->sockaddr, port);
         }
 
         peer->socklen = addr->socklen;
@@ -651,9 +739,30 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
                                        peer->name.data, NGX_SOCKADDR_STRLEN, 1);
 
         peer->host = template->host;
-        peer->server = template->server;
 
-        peer->weight = template->weight;
+        server = host->service.len ? &addr->name : &template->server;
+
+        peer->server.data = ngx_slab_alloc(peers->shpool, server->len);
+        if (peer->server.data == NULL) {
+            ngx_stream_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;
@@ -672,8 +781,25 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx)
         ngx_stream_upstream_zone_set_single(uscf);
     }
 
+    if (host->service.len && peers->next) {
+        ngx_stream_upstream_rr_peers_unlock(peers);
+
+        peers = peers->next;
+        backup = 1;
+
+        ngx_stream_upstream_rr_peers_wlock(peers);
+
+        goto again;
+    }
+
+done:
+
     ngx_stream_upstream_rr_peers_unlock(peers);
 
+    while (++i < ctx->naddrs) {
+        ngx_stream_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);


More information about the nginx-devel mailing list