[PATCH 3 of 9] Upstream: construct upstream peers from DNS SRV records

Aleksei Bavshin a.bavshin at nginx.com
Thu Jun 13 22:28:58 UTC 2024


# HG changeset patch
# User Dmitry Volyntsev <xeioex at nginx.com>
# Date 1458229351 -10800
#      Thu Mar 17 18:42:31 2016 +0300
# Node ID 8b7fcded3983023229de1a6df5e2e0b857ee1bc9
# Parent  a2b1433abda29286189dad01fc3574df235e6aa8
Upstream: construct upstream peers from DNS SRV records.

diff --git a/src/http/modules/ngx_http_upstream_zone_module.c b/src/http/modules/ngx_http_upstream_zone_module.c
--- a/src/http/modules/ngx_http_upstream_zone_module.c
+++ b/src/http/modules/ngx_http_upstream_zone_module.c
@@ -361,6 +361,18 @@ ngx_http_upstream_zone_copy_peer(ngx_htt
             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);
+            }
         }
     }
 
@@ -369,6 +381,10 @@ ngx_http_upstream_zone_copy_peer(ngx_htt
 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);
     }
 
@@ -533,6 +549,7 @@ ngx_http_upstream_zone_resolve_timer(ngx
     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) {
@@ -545,15 +562,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;
@@ -573,6 +603,11 @@ ngx_http_upstream_zone_resolve_handler(n
         ngx_http_upstream_rr_peers_unlock(peers);
 
         ngx_shmtx_lock(&peers->shpool->mutex);
+
+        if (host->service.len) {
+            ngx_slab_free_locked(peers->shpool, host->service.data);
+        }
+
         ngx_slab_free_locked(peers->shpool, host->name.data);
         ngx_slab_free_locked(peers->shpool, host);
         ngx_shmtx_unlock(&peers->shpool->mutex);
@@ -584,11 +619,32 @@ ngx_http_upstream_zone_resolve_handler(n
 
     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);
@@ -604,6 +660,13 @@ ngx_http_upstream_zone_resolve_handler(n
         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];
@@ -611,14 +674,20 @@ ngx_http_upstream_zone_resolve_handler(n
 
     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;
 
@@ -630,14 +699,39 @@ ngx_http_upstream_zone_resolve_handler(n
 
             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;
@@ -656,8 +750,13 @@ ngx_http_upstream_zone_resolve_handler(n
 
         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;
         }
 
@@ -669,21 +768,14 @@ ngx_http_upstream_zone_resolve_handler(n
             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;
@@ -692,9 +784,30 @@ ngx_http_upstream_zone_resolve_handler(n
                                        peer->name.data, NGX_SOCKADDR_STRLEN, 1);
 
         peer->host = template->host;
-        peer->server = template->server;
+
+        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);
 
-        peer->weight = template->weight;
+            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;
@@ -713,8 +826,25 @@ ngx_http_upstream_zone_resolve_handler(n
         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);
     timer = ngx_min(timer, uscf->resolver_timeout);
 
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -6310,6 +6310,19 @@ ngx_http_upstream_server(ngx_conf_t *cf,
             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;
@@ -6325,6 +6338,15 @@ ngx_http_upstream_server(ngx_conf_t *cf,
         /* 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) {
@@ -6340,6 +6362,22 @@ ngx_http_upstream_server(ngx_conf_t *cf,
 
 #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
--- 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
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -187,6 +187,7 @@ ngx_http_upstream_init_round_robin(ngx_c
                 }
 
                 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;
@@ -319,6 +320,7 @@ ngx_http_upstream_init_round_robin(ngx_c
                 }
 
                 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
--- 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;
@@ -156,7 +157,7 @@ ngx_http_upstream_rr_peer_free_locked(ng
     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
--- a/src/stream/ngx_stream_upstream.c
+++ b/src/stream/ngx_stream_upstream.c
@@ -527,6 +527,19 @@ ngx_stream_upstream_server(ngx_conf_t *c
             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;
@@ -541,6 +554,15 @@ ngx_stream_upstream_server(ngx_conf_t *c
         /* 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) {
@@ -552,7 +574,12 @@ ngx_stream_upstream_server(ngx_conf_t *c
         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;
@@ -562,6 +589,22 @@ ngx_stream_upstream_server(ngx_conf_t *c
 
 #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
--- 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
--- a/src/stream/ngx_stream_upstream_round_robin.c
+++ b/src/stream/ngx_stream_upstream_round_robin.c
@@ -194,6 +194,7 @@ ngx_stream_upstream_init_round_robin(ngx
                 }
 
                 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;
@@ -325,6 +326,7 @@ ngx_stream_upstream_init_round_robin(ngx
                 }
 
                 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
--- 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;
@@ -154,7 +155,7 @@ ngx_stream_upstream_rr_peer_free_locked(
     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
--- a/src/stream/ngx_stream_upstream_zone_module.c
+++ b/src/stream/ngx_stream_upstream_zone_module.c
@@ -358,6 +358,18 @@ ngx_stream_upstream_zone_copy_peer(ngx_s
             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);
+            }
         }
     }
 
@@ -366,6 +378,10 @@ ngx_stream_upstream_zone_copy_peer(ngx_s
 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);
     }
 
@@ -531,6 +547,7 @@ ngx_stream_upstream_zone_resolve_timer(n
     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) {
@@ -543,15 +560,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;
@@ -571,6 +601,11 @@ ngx_stream_upstream_zone_resolve_handler
         ngx_stream_upstream_rr_peers_unlock(peers);
 
         ngx_shmtx_lock(&peers->shpool->mutex);
+
+        if (host->service.len) {
+            ngx_slab_free_locked(peers->shpool, host->service.data);
+        }
+
         ngx_slab_free_locked(peers->shpool, host->name.data);
         ngx_slab_free_locked(peers->shpool, host);
         ngx_shmtx_unlock(&peers->shpool->mutex);
@@ -582,11 +617,32 @@ ngx_stream_upstream_zone_resolve_handler
 
     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);
@@ -602,6 +658,13 @@ ngx_stream_upstream_zone_resolve_handler
         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];
@@ -609,14 +672,20 @@ ngx_stream_upstream_zone_resolve_handler
 
     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;
 
@@ -628,14 +697,39 @@ ngx_stream_upstream_zone_resolve_handler
 
             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;
@@ -654,33 +748,32 @@ ngx_stream_upstream_zone_resolve_handler
 
         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;
@@ -689,9 +782,30 @@ ngx_stream_upstream_zone_resolve_handler
                                        peer->name.data, NGX_SOCKADDR_STRLEN, 1);
 
         peer->host = template->host;
-        peer->server = template->server;
+
+        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);
 
-        peer->weight = template->weight;
+            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;
@@ -710,8 +824,25 @@ ngx_stream_upstream_zone_resolve_handler
         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);
     timer = ngx_min(timer, uscf->resolver_timeout);
 


More information about the nginx-devel mailing list