diff src/http/modules/ngx_http_upstream_ip_hash_module.c @ 260:0effe91f6083 NGINX_0_5_0

nginx 0.5.0 *) Change: the parameters in the "%name" form in the "log_format" directive are not supported anymore. *) Change: the "proxy_upstream_max_fails", "proxy_upstream_fail_timeout", "fastcgi_upstream_max_fails", "fastcgi_upstream_fail_timeout", "memcached_upstream_max_fails", and "memcached_upstream_fail_timeout" directives are not supported anymore. *) Feature: the "server" directive in the "upstream" context supports the "max_fails", "fail_timeout", and "down" parameters. *) Feature: the "ip_hash" directive inside the "upstream" block. *) Feature: the WAIT status in the "Auth-Status" header line of the IMAP/POP3 proxy authentication server response. *) Bugfix: nginx could not be built on 64-bit platforms; bug appeared in 0.4.14.
author Igor Sysoev <http://sysoev.ru>
date Mon, 04 Dec 2006 00:00:00 +0300
parents
children 052a7b1d40e5
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
@@ -0,0 +1,228 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    /* the round robin data must be first */
+    ngx_http_upstream_rr_peer_data_t   rrp;
+
+    ngx_uint_t                         hash;
+
+    /* AF_INET only */
+    u_char                             addr[3];
+
+    u_char                             tries;
+
+    ngx_event_get_peer_pt              get_rr_peer;
+} ngx_http_upstream_ip_hash_peer_data_t;
+
+
+static ngx_int_t ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
+    ngx_http_upstream_srv_conf_t *us);
+static ngx_int_t ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc,
+    void *data);
+static char *ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
+
+static ngx_command_t  ngx_http_upstream_ip_hash_commands[] = {
+
+    { ngx_string("ip_hash"),
+      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
+      ngx_http_upstream_ip_hash,
+      0,
+      0,
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_upstream_ip_hash_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_ip_hash_module = {
+    NGX_MODULE_V1,
+    &ngx_http_upstream_ip_hash_module_ctx, /* module context */
+    ngx_http_upstream_ip_hash_commands,    /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+ngx_int_t
+ngx_http_upstream_init_ip_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
+{
+    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    us->peer.init = ngx_http_upstream_init_ip_hash_peer;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
+    ngx_http_upstream_srv_conf_t *us)
+{
+    struct sockaddr_in                     *sin;
+    ngx_http_upstream_ip_hash_peer_data_t  *iphp;
+
+    iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t));
+    if (iphp == NULL) {
+        return NGX_ERROR;
+    }
+
+    r->upstream->peer.data = &iphp->rrp;
+
+    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;
+
+    /* AF_INET only */
+    sin = (struct sockaddr_in *) r->connection->sockaddr;
+    iphp->addr[0] = (u_char) ((sin->sin_addr.s_addr >> 24) & 0xff);
+    iphp->addr[1] = (u_char) ((sin->sin_addr.s_addr >> 16) & 0xff);
+    iphp->addr[2] = (u_char) ((sin->sin_addr.s_addr >> 8) & 0xff);
+
+    iphp->hash = 89;
+    iphp->tries = 0;
+    iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
+{
+    ngx_http_upstream_ip_hash_peer_data_t  *iphp = data;
+
+    time_t                        now;
+    uintptr_t                     m;
+    ngx_uint_t                    i, n, p, hash;
+    ngx_http_upstream_rr_peer_t  *peer;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+                   "get ip hash peer, try: %ui", pc->tries);
+
+    /* TODO: cached */
+
+    if (iphp->tries > 20 || iphp->rrp.peers->number == 1) {
+        return iphp->get_rr_peer(pc, &iphp->rrp);
+    }
+
+    now = ngx_time();
+
+    pc->cached = 0;
+    pc->connection = NULL;
+
+    hash = iphp->hash;
+
+    for ( ;; ) {
+
+        for (i = 0; i < 3; i++) {
+            hash = (hash * 113 + iphp->addr[i]) % 6271;
+        }
+
+        p = hash % iphp->rrp.peers->number;
+
+        n = p / (8 * sizeof(uintptr_t));
+        m = 1 << p % (8 * sizeof(uintptr_t));
+
+        if (!(iphp->rrp.tried[n] & m)) {
+
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+                           "get ip hash peer, hash: %ui %04XA", p, m);
+
+            peer = &iphp->rrp.peers->peer[p];
+
+            /* ngx_lock_mutex(iphp->rrp.peers->mutex); */
+
+            if (!peer->down) {
+
+		if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
+		    break;
+		}
+
+		if (now - peer->accessed > peer->fail_timeout) {
+		    peer->fails = 0;
+		    break;
+		}
+
+            } else {
+                iphp->rrp.tried[n] |= m;
+            }
+
+            /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
+
+            pc->tries--;
+        }
+
+        if (++iphp->tries >= 20) {
+            return iphp->get_rr_peer(pc, &iphp->rrp);
+        }
+    }
+
+    pc->sockaddr = peer->sockaddr;
+    pc->socklen = peer->socklen;
+    pc->name = &peer->name;
+#if (NGX_SSL)
+    pc->ssl_session = peer->ssl_session;
+#endif
+
+    /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
+
+    iphp->rrp.tried[n] |= m;
+    iphp->hash = hash;
+
+    return NGX_OK;
+}
+
+
+static char *
+ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_upstream_srv_conf_t  *uscf;
+
+    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+
+    uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;
+
+    uscf->flags = NGX_HTTP_UPSTREAM_CREATE
+                  |NGX_HTTP_UPSTREAM_MAX_FAILS
+                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
+                  |NGX_HTTP_UPSTREAM_DOWN;
+
+    return NGX_CONF_OK;
+}