2 * Hash a variable to choose an upstream server.
4 * Copyright (C) Evan Miller
6 * This module can be distributed under the same terms as Nginx itself.
10 #include <ngx_config.h>
14 #define ngx_bitvector_index(index) index / (8 * sizeof(uintptr_t))
15 #define ngx_bitvector_bit(index) (uintptr_t) 1 << index % (8 * sizeof(uintptr_t))
18 struct sockaddr *sockaddr;
21 } ngx_http_upstream_hash_peer_t;
25 ngx_http_upstream_hash_peer_t peer[0];
26 } ngx_http_upstream_hash_peers_t;
29 ngx_http_upstream_hash_peers_t *peers;
31 ngx_str_t current_key;
32 ngx_str_t original_key;
35 } ngx_http_upstream_hash_peer_data_t;
38 static ngx_int_t ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,
39 ngx_http_upstream_srv_conf_t *us);
40 static ngx_int_t ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc,
42 static void ngx_http_upstream_free_hash_peer(ngx_peer_connection_t *pc,
43 void *data, ngx_uint_t state);
44 static char *ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd,
46 static char *ngx_http_upstream_hash_again(ngx_conf_t *cf, ngx_command_t *cmd,
48 static ngx_int_t ngx_http_upstream_init_hash(ngx_conf_t *cf,
49 ngx_http_upstream_srv_conf_t *us);
50 static ngx_uint_t ngx_http_upstream_hash_crc32(u_char *keydata, size_t keylen);
53 static ngx_command_t ngx_http_upstream_hash_commands[] = {
55 NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
56 ngx_http_upstream_hash,
61 { ngx_string("hash_again"),
62 NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
63 ngx_http_upstream_hash_again,
72 static ngx_http_module_t ngx_http_upstream_hash_module_ctx = {
73 NULL, /* preconfiguration */
74 NULL, /* postconfiguration */
76 NULL, /* create main configuration */
77 NULL, /* init main configuration */
79 NULL, /* create server configuration */
80 NULL, /* merge server configuration */
82 NULL, /* create location configuration */
83 NULL /* merge location configuration */
87 ngx_module_t ngx_http_upstream_hash_module = {
89 &ngx_http_upstream_hash_module_ctx, /* module context */
90 ngx_http_upstream_hash_commands, /* module directives */
91 NGX_HTTP_MODULE, /* module type */
92 NULL, /* init master */
93 NULL, /* init module */
94 NULL, /* init process */
95 NULL, /* init thread */
96 NULL, /* exit thread */
97 NULL, /* exit process */
98 NULL, /* exit master */
104 ngx_http_upstream_init_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
107 ngx_http_upstream_server_t *server;
108 ngx_http_upstream_hash_peers_t *peers;
110 us->peer.init = ngx_http_upstream_init_hash_peer;
117 server = us->servers->elts;
119 for (n = 0, i = 0; i < us->servers->nelts; i++) {
120 n += server[i].naddrs;
123 peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_hash_peers_t)
124 + sizeof(ngx_http_upstream_hash_peer_t) * n);
132 /* one hostname can have multiple IP addresses in DNS */
133 for (n = 0, i = 0; i < us->servers->nelts; i++) {
134 for (j = 0; j < server[i].naddrs; j++, n++) {
135 peers->peer[n].sockaddr = server[i].addrs[j].sockaddr;
136 peers->peer[n].socklen = server[i].addrs[j].socklen;
137 peers->peer[n].name = server[i].addrs[j].name;
141 us->peer.data = peers;
148 ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,
149 ngx_http_upstream_srv_conf_t *us)
151 ngx_http_upstream_hash_peer_data_t *uhpd;
155 if (ngx_http_script_run(r, &val, us->lengths, 0, us->values) == NULL) {
159 uhpd = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_hash_peer_data_t)
160 + sizeof(uintptr_t) *
161 ((ngx_http_upstream_hash_peers_t *)us->peer.data)->number /
162 (8 * sizeof(uintptr_t)));
167 r->upstream->peer.data = uhpd;
169 uhpd->peers = us->peer.data;
171 r->upstream->peer.free = ngx_http_upstream_free_hash_peer;
172 r->upstream->peer.get = ngx_http_upstream_get_hash_peer;
173 r->upstream->peer.tries = us->retries + 1;
175 /* must be big enough for the retry keys */
176 if ((uhpd->current_key.data = ngx_pcalloc(r->pool, NGX_ATOMIC_T_LEN + val.len)) == NULL) {
180 ngx_memcpy(uhpd->current_key.data, val.data, val.len);
181 uhpd->current_key.len = val.len;
182 uhpd->original_key = val;
184 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
185 "upstream_hash: hashing \"%V\"", &val);
187 uhpd->hash = ngx_http_upstream_hash_crc32(val.data, val.len);
194 ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
196 ngx_http_upstream_hash_peer_data_t *uhpd = data;
197 ngx_http_upstream_hash_peer_t *peer;
198 ngx_uint_t peer_index;
200 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
201 "upstream_hash: get upstream request hash peer try %ui", pc->tries);
204 pc->connection = NULL;
206 peer_index = uhpd->hash % uhpd->peers->number;
208 peer = &uhpd->peers->peer[peer_index];
210 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
211 "upstream_hash: chose peer %ui w/ hash %ui", peer_index, uhpd->hash);
213 pc->sockaddr = peer->sockaddr;
214 pc->socklen = peer->socklen;
215 pc->name = &peer->name;
220 /* retry implementation is PECL memcache compatible */
222 ngx_http_upstream_free_hash_peer(ngx_peer_connection_t *pc, void *data,
225 ngx_http_upstream_hash_peer_data_t *uhpd = data;
228 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
229 "upstream_hash: free upstream hash peer try %ui", pc->tries);
231 if (state & (NGX_PEER_FAILED|NGX_PEER_NEXT)
234 current = uhpd->hash % uhpd->peers->number;
236 uhpd->tried[ngx_bitvector_index(current)] |= ngx_bitvector_bit(current);
239 uhpd->current_key.len = ngx_sprintf(uhpd->current_key.data, "%d%V",
240 ++uhpd->try_i, &uhpd->original_key) - uhpd->current_key.data;
241 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
242 "upstream_hash: hashing \"%V\"", &uhpd->current_key);
243 uhpd->hash += ngx_http_upstream_hash_crc32(uhpd->current_key.data,
244 uhpd->current_key.len);
245 current = uhpd->hash % uhpd->peers->number;
246 } while ((uhpd->tried[ngx_bitvector_index(current)] & ngx_bitvector_bit(current))
251 /* bit-shift, bit-mask, and non-zero requirement are for libmemcache compatibility */
253 ngx_http_upstream_hash_crc32(u_char *keydata, size_t keylen)
255 ngx_uint_t crc32 = (ngx_crc32_short(keydata, keylen) >> 16) & 0x7fff;
256 return crc32 ? crc32 : 1;
260 ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
262 ngx_http_upstream_srv_conf_t *uscf;
263 ngx_http_script_compile_t sc;
265 ngx_array_t *vars_lengths, *vars_values;
267 value = cf->args->elts;
269 ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
275 sc.source = &value[1];
276 sc.lengths = &vars_lengths;
277 sc.values = &vars_values;
278 sc.complete_lengths = 1;
279 sc.complete_values = 1;
281 if (ngx_http_script_compile(&sc) != NGX_OK) {
282 return NGX_CONF_ERROR;
285 uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
287 uscf->peer.init_upstream = ngx_http_upstream_init_hash;
289 uscf->flags = NGX_HTTP_UPSTREAM_CREATE;
291 uscf->values = vars_values->elts;
292 uscf->lengths = vars_lengths->elts;
298 ngx_http_upstream_hash_again(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
300 ngx_http_upstream_srv_conf_t *uscf;
305 uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
307 value = cf->args->elts;
309 n = ngx_atoi(value[1].data, value[1].len);
311 if (n == NGX_ERROR || n < 0) {
312 return "invalid number";