├── CHANGES ├── CREDITS ├── README.md ├── config ├── ngx_http_upstream_hash_module.c └── t ├── clean.sh ├── hashtest.php ├── nginx.conf ├── nginx └── conf │ └── nginx.conf └── restart.sh /CHANGES: -------------------------------------------------------------------------------- 1 | Changes with upstream_hash 0.3.2 03 Jun 2010 2 | 3 | *) Support for servers marked as down. Thanks to Jack Lindamood of Facebook. 4 | 5 | 6 | Changes with upstream_hash 0.3.1 19 Oct 2009 7 | 8 | *) Update nginx.patch to work with more recent versions of nginx 9 | 10 | 11 | Changes with upstream_hash 0.3 06 Aug 2008 12 | 13 | *) Bugfix: infinite loop when retrying after a 404 and the "not_found" 14 | flag of *_next_upstream was set. 15 | 16 | *) Change: no more "hash_method" directive. Hash method is always CRC-32. 17 | 18 | *) Change: failover strategy is compatible with PECL Memcache 19 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | Most development (so far) by Evan Miller. 2 | 3 | PECL Memcache compatibility sponsored by Spil Games, Inc. 4 | [http://www.spilgames.com/] 5 | 6 | Support for servers marked as "down" contributed by Jack Lindamood of Facebook. 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NOTE: This module is obsolete as of Nginx 1.7.2, which includes the [hash](http://nginx.org/r/hash) directive. This repository remains for historical interest only. 2 | == 3 | 4 | ngx_http_upstream_hash_module 5 | -- 6 | 7 | Installation: 8 | 9 | cd nginx-0.7.62 # or whatever 10 | ./configure --add-module=/path/to/this/directory 11 | make 12 | make install 13 | 14 | Usage: 15 | 16 | upstream backend { 17 | ... 18 | hash $request_uri; 19 | hash_again 10; # default 0 20 | } 21 | 22 | Works the same on 32-bit and 64-bit systems. 23 | 24 | See http://wiki.nginx.org/NginxHttpUpstreamRequestHashModule for more details. 25 | 26 | Questions/patches to Evan Miller, emmiller@gmail.com. 27 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_upstream_hash_module 2 | HTTP_MODULES="$HTTP_MODULES ngx_http_upstream_hash_module" 3 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_upstream_hash_module.c" 4 | have=NGX_HTTP_UPSTREAM_HASH . auto/have 5 | -------------------------------------------------------------------------------- /ngx_http_upstream_hash_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Hash a variable to choose an upstream server. 3 | * 4 | * Copyright (C) Evan Miller 5 | * 6 | * This module can be distributed under the same terms as Nginx itself. 7 | */ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #if (NGX_HTTP_HEALTHCHECK) 15 | #include 16 | #endif 17 | 18 | #define ngx_bitvector_index(index) (index / (8 * sizeof(uintptr_t))) 19 | #define ngx_bitvector_bit(index) ((uintptr_t) 1 << (index % (8 * sizeof(uintptr_t)))) 20 | 21 | 22 | typedef struct { 23 | ngx_array_t *values; 24 | ngx_array_t *lengths; 25 | ngx_uint_t retries; 26 | } ngx_http_upstream_hash_conf_t; 27 | 28 | 29 | typedef struct { 30 | struct sockaddr *sockaddr; 31 | socklen_t socklen; 32 | ngx_str_t name; 33 | ngx_uint_t down; 34 | ngx_int_t weight; 35 | #if (NGX_HTTP_HEALTHCHECK) 36 | ngx_int_t health_index; 37 | #endif 38 | #if (NGX_HTTP_SSL) 39 | ngx_ssl_session_t *ssl_session; /* local to a process */ 40 | #endif 41 | } ngx_http_upstream_hash_peer_t; 42 | 43 | typedef struct { 44 | ngx_uint_t number; 45 | ngx_uint_t total_weight; 46 | unsigned weighted:1; 47 | ngx_http_upstream_hash_peer_t peer[0]; 48 | } ngx_http_upstream_hash_peers_t; 49 | 50 | typedef struct { 51 | ngx_http_upstream_hash_peers_t *peers; 52 | uint32_t hash; 53 | ngx_str_t current_key; 54 | ngx_str_t original_key; 55 | ngx_uint_t try_i; 56 | uintptr_t tried[1]; 57 | } ngx_http_upstream_hash_peer_data_t; 58 | 59 | 60 | static ngx_uint_t ngx_http_upstream_get_hash_peer_index( 61 | ngx_http_upstream_hash_peer_data_t *uhpd); 62 | static void ngx_http_upstream_hash_next_peer(ngx_http_upstream_hash_peer_data_t *uhpd, 63 | ngx_uint_t *tries, ngx_log_t *log); 64 | static ngx_int_t ngx_http_upstream_init_hash_peer(ngx_http_request_t *r, 65 | ngx_http_upstream_srv_conf_t *us); 66 | static ngx_int_t ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, 67 | void *data); 68 | static void ngx_http_upstream_free_hash_peer(ngx_peer_connection_t *pc, 69 | void *data, ngx_uint_t state); 70 | #if (NGX_HTTP_SSL) 71 | static ngx_int_t ngx_http_upstream_set_hash_peer_session(ngx_peer_connection_t *pc, 72 | void *data); 73 | static void ngx_http_upstream_save_hash_peer_session(ngx_peer_connection_t *pc, 74 | void *data); 75 | #endif 76 | static char *ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, 77 | void *conf); 78 | static char *ngx_http_upstream_hash_again(ngx_conf_t *cf, ngx_command_t *cmd, 79 | void *conf); 80 | static ngx_int_t ngx_http_upstream_init_hash(ngx_conf_t *cf, 81 | ngx_http_upstream_srv_conf_t *us); 82 | static ngx_uint_t ngx_http_upstream_hash_crc32(u_char *keydata, size_t keylen); 83 | static void *ngx_http_upstream_hash_create_conf(ngx_conf_t *cf); 84 | 85 | 86 | static ngx_command_t ngx_http_upstream_hash_commands[] = { 87 | { ngx_string("hash"), 88 | NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1, 89 | ngx_http_upstream_hash, 90 | 0, 91 | 0, 92 | NULL }, 93 | 94 | { ngx_string("hash_again"), 95 | NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1, 96 | ngx_http_upstream_hash_again, 97 | 0, 98 | 0, 99 | NULL }, 100 | 101 | ngx_null_command 102 | }; 103 | 104 | 105 | static ngx_http_module_t ngx_http_upstream_hash_module_ctx = { 106 | NULL, /* preconfiguration */ 107 | NULL, /* postconfiguration */ 108 | 109 | NULL, /* create main configuration */ 110 | NULL, /* init main configuration */ 111 | 112 | ngx_http_upstream_hash_create_conf, /* create server configuration */ 113 | NULL, /* merge server configuration */ 114 | 115 | NULL, /* create location configuration */ 116 | NULL /* merge location configuration */ 117 | }; 118 | 119 | 120 | ngx_module_t ngx_http_upstream_hash_module = { 121 | NGX_MODULE_V1, 122 | &ngx_http_upstream_hash_module_ctx, /* module context */ 123 | ngx_http_upstream_hash_commands, /* module directives */ 124 | NGX_HTTP_MODULE, /* module type */ 125 | NULL, /* init master */ 126 | NULL, /* init module */ 127 | NULL, /* init process */ 128 | NULL, /* init thread */ 129 | NULL, /* exit thread */ 130 | NULL, /* exit process */ 131 | NULL, /* exit master */ 132 | NGX_MODULE_V1_PADDING 133 | }; 134 | 135 | 136 | static ngx_uint_t 137 | ngx_http_upstream_get_hash_peer_index(ngx_http_upstream_hash_peer_data_t *uhpd) 138 | { 139 | ngx_int_t w; 140 | ngx_uint_t i; 141 | 142 | if (!uhpd->peers->weighted) { 143 | return uhpd->hash % uhpd->peers->number; 144 | } 145 | 146 | w = uhpd->hash % uhpd->peers->total_weight; 147 | 148 | for (i = 0; i < uhpd->peers->number; i++) { 149 | w -= uhpd->peers->peer[i].weight; 150 | if (w < 0) { 151 | return i; 152 | } 153 | } 154 | 155 | /* unreachable */ 156 | 157 | return 0; 158 | } 159 | 160 | 161 | static ngx_int_t 162 | ngx_http_upstream_init_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) 163 | { 164 | ngx_uint_t i, j, n, w; 165 | ngx_http_upstream_server_t *server; 166 | ngx_http_upstream_hash_peers_t *peers; 167 | #if (NGX_HTTP_HEALTHCHECK) 168 | ngx_int_t health_index; 169 | #endif 170 | us->peer.init = ngx_http_upstream_init_hash_peer; 171 | 172 | if (!us->servers) { 173 | return NGX_ERROR; 174 | } 175 | 176 | server = us->servers->elts; 177 | 178 | n = 0; 179 | w = 0; 180 | for (i = 0; i < us->servers->nelts; i++) { 181 | if (server[i].backup) 182 | continue; 183 | 184 | n += server[i].naddrs; 185 | w += server[i].naddrs * server[i].weight; 186 | } 187 | 188 | if (n == 0) { 189 | return NGX_ERROR; 190 | } 191 | 192 | peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_hash_peers_t) 193 | + sizeof(ngx_http_upstream_hash_peer_t) * n); 194 | 195 | if (peers == NULL) { 196 | return NGX_ERROR; 197 | } 198 | 199 | peers->number = n; 200 | peers->weighted = (w != n); 201 | peers->total_weight = w; 202 | 203 | n = 0; 204 | /* one hostname can have multiple IP addresses in DNS */ 205 | for (i = 0; i < us->servers->nelts; i++) { 206 | for (j = 0; j < server[i].naddrs; j++) { 207 | if (server[i].backup) 208 | continue; 209 | peers->peer[n].sockaddr = server[i].addrs[j].sockaddr; 210 | peers->peer[n].socklen = server[i].addrs[j].socklen; 211 | peers->peer[n].name = server[i].addrs[j].name; 212 | peers->peer[n].down = server[i].down; 213 | peers->peer[n].weight = server[i].weight; 214 | #if (NGX_HTTP_HEALTHCHECK) 215 | if (!server[i].down) { 216 | health_index = 217 | ngx_http_healthcheck_add_peer(us, 218 | &server[i].addrs[j], cf->pool); 219 | if (health_index == NGX_ERROR) { 220 | return NGX_ERROR; 221 | } 222 | peers->peer[n].health_index = health_index; 223 | } 224 | #endif 225 | n++; 226 | } 227 | } 228 | 229 | us->peer.data = peers; 230 | 231 | return NGX_OK; 232 | } 233 | 234 | 235 | static ngx_int_t 236 | ngx_http_upstream_init_hash_peer(ngx_http_request_t *r, 237 | ngx_http_upstream_srv_conf_t *us) 238 | { 239 | ngx_http_upstream_hash_peer_data_t *uhpd; 240 | ngx_http_upstream_hash_conf_t *uhcf; 241 | 242 | ngx_str_t val; 243 | 244 | uhcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module); 245 | 246 | if (ngx_http_script_run(r, &val, uhcf->lengths, 0, uhcf->values) == NULL) { 247 | return NGX_ERROR; 248 | } 249 | 250 | uhpd = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_hash_peer_data_t) 251 | + sizeof(uintptr_t) * 252 | ((ngx_http_upstream_hash_peers_t *)us->peer.data)->number / 253 | (8 * sizeof(uintptr_t))); 254 | if (uhpd == NULL) { 255 | return NGX_ERROR; 256 | } 257 | 258 | r->upstream->peer.data = uhpd; 259 | 260 | uhpd->peers = us->peer.data; 261 | 262 | r->upstream->peer.free = ngx_http_upstream_free_hash_peer; 263 | r->upstream->peer.get = ngx_http_upstream_get_hash_peer; 264 | r->upstream->peer.tries = uhcf->retries + 1; 265 | #if (NGX_HTTP_SSL) 266 | r->upstream->peer.set_session = ngx_http_upstream_set_hash_peer_session; 267 | r->upstream->peer.save_session = ngx_http_upstream_save_hash_peer_session; 268 | #endif 269 | 270 | /* must be big enough for the retry keys */ 271 | if ((uhpd->current_key.data = ngx_pcalloc(r->pool, NGX_ATOMIC_T_LEN + val.len)) == NULL) { 272 | return NGX_ERROR; 273 | } 274 | 275 | ngx_memcpy(uhpd->current_key.data, val.data, val.len); 276 | uhpd->current_key.len = val.len; 277 | uhpd->original_key = val; 278 | uhpd->hash = ngx_http_upstream_hash_crc32(uhpd->current_key.data, uhpd->current_key.len); 279 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 280 | "upstream_hash: hashed \"%V\" to %ui", &uhpd->current_key, 281 | ngx_http_upstream_get_hash_peer_index(uhpd)); 282 | uhpd->try_i = 0; 283 | 284 | /* In case this one is marked down */ 285 | ngx_http_upstream_hash_next_peer(uhpd, &r->upstream->peer.tries, r->connection->log); 286 | if ((ngx_int_t)r->upstream->peer.tries == -1) { 287 | return NGX_ERROR; 288 | } 289 | 290 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 291 | "upstream_hash: Starting with %ui", 292 | ngx_http_upstream_get_hash_peer_index(uhpd)); 293 | 294 | 295 | return NGX_OK; 296 | } 297 | 298 | 299 | static ngx_int_t 300 | ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data) 301 | { 302 | ngx_http_upstream_hash_peer_data_t *uhpd = data; 303 | ngx_http_upstream_hash_peer_t *peer; 304 | ngx_uint_t peer_index; 305 | 306 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, 307 | "upstream_hash: get upstream request hash peer try %ui", pc->tries); 308 | 309 | pc->cached = 0; 310 | pc->connection = NULL; 311 | 312 | peer_index = ngx_http_upstream_get_hash_peer_index(uhpd); 313 | 314 | peer = &uhpd->peers->peer[peer_index]; 315 | 316 | 317 | ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, 318 | "upstream_hash: chose peer %ui w/ hash %ui for tries %ui", peer_index, uhpd->hash, pc->tries); 319 | 320 | pc->sockaddr = peer->sockaddr; 321 | pc->socklen = peer->socklen; 322 | pc->name = &peer->name; 323 | 324 | return NGX_OK; 325 | } 326 | 327 | /* retry implementation is PECL memcache compatible */ 328 | static void 329 | ngx_http_upstream_free_hash_peer(ngx_peer_connection_t *pc, void *data, 330 | ngx_uint_t state) 331 | { 332 | ngx_http_upstream_hash_peer_data_t *uhpd = data; 333 | ngx_uint_t current; 334 | 335 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, 336 | "upstream_hash: free upstream hash peer try %ui", pc->tries); 337 | 338 | if (state & (NGX_PEER_FAILED|NGX_PEER_NEXT) 339 | && (ngx_int_t) pc->tries > 0) { 340 | current = ngx_http_upstream_get_hash_peer_index(uhpd); 341 | 342 | uhpd->tried[ngx_bitvector_index(current)] |= ngx_bitvector_bit(current); 343 | ngx_http_upstream_hash_next_peer(uhpd, &pc->tries, pc->log); 344 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, 345 | "upstream_hash: Using %ui because %ui failed", 346 | ngx_http_upstream_get_hash_peer_index(uhpd), current); 347 | } else { 348 | pc->tries = 0; 349 | } 350 | } 351 | 352 | #if (NGX_HTTP_SSL) 353 | static ngx_int_t 354 | ngx_http_upstream_set_hash_peer_session(ngx_peer_connection_t *pc, void *data) { 355 | ngx_http_upstream_hash_peer_data_t *uhpd = data; 356 | 357 | ngx_int_t rc; 358 | ngx_ssl_session_t *ssl_session; 359 | ngx_http_upstream_hash_peer_t *peer; 360 | ngx_uint_t current; 361 | 362 | current = ngx_http_upstream_get_hash_peer_index(uhpd); 363 | 364 | peer = &uhpd->peers->peer[current]; 365 | 366 | /* TODO: threads only mutex */ 367 | /* ngx_lock_mutex(rrp->peers->mutex); */ 368 | 369 | ssl_session = peer->ssl_session; 370 | 371 | rc = ngx_ssl_set_session(pc->connection, ssl_session); 372 | 373 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, 374 | "set session: %p:%d", 375 | ssl_session, ssl_session ? ssl_session->references : 0); 376 | 377 | /* ngx_unlock_mutex(rrp->peers->mutex); */ 378 | 379 | return rc; 380 | } 381 | 382 | static void 383 | ngx_http_upstream_save_hash_peer_session(ngx_peer_connection_t *pc, void *data) { 384 | ngx_http_upstream_hash_peer_data_t *uhpd = data; 385 | ngx_ssl_session_t *old_ssl_session, *ssl_session; 386 | ngx_http_upstream_hash_peer_t *peer; 387 | ngx_uint_t current; 388 | 389 | ssl_session = ngx_ssl_get_session(pc->connection); 390 | 391 | if (ssl_session == NULL) { 392 | return; 393 | } 394 | 395 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, 396 | "save session: %p:%d", ssl_session, ssl_session->references); 397 | 398 | current = ngx_http_upstream_get_hash_peer_index(uhpd); 399 | 400 | peer = &uhpd->peers->peer[current]; 401 | 402 | /* TODO: threads only mutex */ 403 | /* ngx_lock_mutex(rrp->peers->mutex); */ 404 | 405 | old_ssl_session = peer->ssl_session; 406 | peer->ssl_session = ssl_session; 407 | 408 | /* ngx_unlock_mutex(rrp->peers->mutex); */ 409 | 410 | if (old_ssl_session) { 411 | 412 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, 413 | "old session: %p:%d", 414 | old_ssl_session, old_ssl_session->references); 415 | 416 | /* TODO: may block */ 417 | 418 | ngx_ssl_free_session(old_ssl_session); 419 | } 420 | } 421 | #endif 422 | 423 | static void ngx_http_upstream_hash_next_peer(ngx_http_upstream_hash_peer_data_t *uhpd, 424 | ngx_uint_t *tries, ngx_log_t *log) { 425 | 426 | ngx_uint_t current; 427 | current = ngx_http_upstream_get_hash_peer_index(uhpd); 428 | // Loop while there is a try left, we're on one we haven't tried, and 429 | // the current peer isn't marked down 430 | while ((*tries)-- && ( 431 | (uhpd->tried[ngx_bitvector_index(current)] & ngx_bitvector_bit(current)) 432 | || uhpd->peers->peer[current].down 433 | #if (NGX_HTTP_HEALTHCHECK) 434 | || ngx_http_healthcheck_is_down(uhpd->peers->peer[current].health_index, log) 435 | #endif 436 | )) { 437 | uhpd->current_key.len = ngx_sprintf(uhpd->current_key.data, "%d%V", 438 | ++uhpd->try_i, &uhpd->original_key) - uhpd->current_key.data; 439 | uhpd->hash += ngx_http_upstream_hash_crc32(uhpd->current_key.data, 440 | uhpd->current_key.len); 441 | current = ngx_http_upstream_get_hash_peer_index(uhpd); 442 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, 443 | "upstream_hash: hashed \"%V\" to %ui", &uhpd->current_key, current); 444 | } 445 | } 446 | 447 | /* bit-shift, bit-mask, and non-zero requirement are for libmemcache compatibility */ 448 | static ngx_uint_t 449 | ngx_http_upstream_hash_crc32(u_char *keydata, size_t keylen) 450 | { 451 | ngx_uint_t crc32 = (ngx_crc32_short(keydata, keylen) >> 16) & 0x7fff; 452 | return crc32 ? crc32 : 1; 453 | } 454 | 455 | static char * 456 | ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 457 | { 458 | ngx_http_upstream_srv_conf_t *uscf; 459 | ngx_http_script_compile_t sc; 460 | ngx_str_t *value; 461 | ngx_array_t *vars_lengths, *vars_values; 462 | ngx_http_upstream_hash_conf_t *uhcf; 463 | 464 | value = cf->args->elts; 465 | 466 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); 467 | 468 | vars_lengths = NULL; 469 | vars_values = NULL; 470 | 471 | sc.cf = cf; 472 | sc.source = &value[1]; 473 | sc.lengths = &vars_lengths; 474 | sc.values = &vars_values; 475 | sc.complete_lengths = 1; 476 | sc.complete_values = 1; 477 | 478 | if (ngx_http_script_compile(&sc) != NGX_OK) { 479 | return NGX_CONF_ERROR; 480 | } 481 | 482 | uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); 483 | 484 | uscf->peer.init_upstream = ngx_http_upstream_init_hash; 485 | 486 | uscf->flags = NGX_HTTP_UPSTREAM_CREATE 487 | |NGX_HTTP_UPSTREAM_WEIGHT 488 | |NGX_HTTP_UPSTREAM_DOWN; 489 | 490 | uhcf = ngx_http_conf_upstream_srv_conf(uscf, ngx_http_upstream_hash_module); 491 | 492 | uhcf->values = vars_values->elts; 493 | uhcf->lengths = vars_lengths->elts; 494 | 495 | return NGX_CONF_OK; 496 | } 497 | 498 | 499 | static void * 500 | ngx_http_upstream_hash_create_conf(ngx_conf_t *cf) 501 | { 502 | ngx_http_upstream_hash_conf_t *conf; 503 | 504 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_hash_conf_t)); 505 | if (conf == NULL) { 506 | return NULL; 507 | } 508 | 509 | /* 510 | * set by ngx_pcalloc(): 511 | * conf->lengths; 512 | * conf->values = NULL; 513 | */ 514 | 515 | conf->retries = 0; 516 | 517 | return conf; 518 | } 519 | 520 | 521 | static char * 522 | ngx_http_upstream_hash_again(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 523 | { 524 | ngx_http_upstream_srv_conf_t *uscf; 525 | ngx_http_upstream_hash_conf_t *uhcf; 526 | ngx_int_t n; 527 | ngx_str_t *value; 528 | 529 | uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); 530 | 531 | value = cf->args->elts; 532 | 533 | n = ngx_atoi(value[1].data, value[1].len); 534 | 535 | if (n == NGX_ERROR || n < 0) { 536 | return "invalid number"; 537 | } 538 | 539 | uhcf = ngx_http_conf_upstream_srv_conf(uscf, ngx_http_upstream_hash_module); 540 | uhcf->retries = n; 541 | 542 | return NGX_CONF_OK; 543 | } 544 | -------------------------------------------------------------------------------- /t/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -r nginx 4 | mkdir -p nginx/conf/ 5 | cp nginx.conf nginx/conf/ 6 | -------------------------------------------------------------------------------- /t/hashtest.php: -------------------------------------------------------------------------------- 1 | addServer("localhost", 11211 + $i); 25 | } 26 | 27 | $test_vals = array( 28 | 'quux' => 'baz', 29 | 'foo' => 'bar', 30 | 'shazam' => 'kerpow!', 31 | 'verbum' => 'word', 32 | 'felix' => 'happy', 33 | 'ren' => 'stimpy', 34 | 'Frank' => 'Julie', 35 | 'peanuts' => 'cracker jacks', 36 | 'all-gaul' => 'is divided into three parts', 37 | 'the-more-tests-the-better' => 'i says', 38 | 'adsfasw' => 'QA#(@()!@*$$*!!', 39 | 'Swing-Low' => 'Sweet Cadillac', 40 | 'can-has-exclamations!' => 'but no spaces or percents', 41 | "Smile_if_you_like_UTF-8_\xa6\x3a" => "\xa6\x3b", 42 | "8103*$)&^#@*^@!&!)*!_(#" => "whew" 43 | ); 44 | 45 | $curl = curl_init(); 46 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 47 | 48 | foreach ($test_vals as $k => $v ) { 49 | assert_equal($memcache->set("/$k", $v), TRUE, "Setting key \"$k\" via PECL"); 50 | $memcache_url = rawurlencode($k); 51 | curl_setopt($curl, CURLOPT_URL, "$base_url$memcache_url"); 52 | assert_equal($v, curl_exec($curl), "Fetching key \"$k\" via Nginx"); 53 | } 54 | 55 | curl_close($curl); 56 | 57 | if ($failures > 0) { 58 | echo "$passes tests paseed, $failures tests failed\n"; 59 | } else { 60 | echo "All $passes tests passed\n"; 61 | } 62 | -------------------------------------------------------------------------------- /t/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | #user nobody; 3 | worker_processes 1; 4 | 5 | #error_log logs/error.log; 6 | error_log logs/error.log debug; 7 | #error_log logs/error.log notice; 8 | #error_log logs/error.log info; 9 | 10 | #pid logs/nginx.pid; 11 | 12 | 13 | events { 14 | worker_connections 1024; 15 | } 16 | 17 | 18 | http { 19 | include mime.types; 20 | default_type application/octet-stream; 21 | 22 | #log_format main '$remote_addr - $remote_user [$time_local] $request ' 23 | # '"$status" $body_bytes_sent "$http_referer" ' 24 | # '"$http_user_agent" "$http_x_forwarded_for"'; 25 | 26 | #access_log logs/access.log main; 27 | 28 | sendfile on; 29 | #tcp_nopush on; 30 | 31 | #keepalive_timeout 0; 32 | keepalive_timeout 65; 33 | 34 | #gzip on; 35 | 36 | upstream backend { 37 | # active 38 | server localhost:11211; 39 | server localhost:11212; 40 | server localhost:11213; 41 | server localhost:11214; 42 | server localhost:11215; 43 | 44 | # dead 45 | server localhost:11216; 46 | server localhost:11217; 47 | server localhost:11218; 48 | server localhost:11219; 49 | server localhost:11220; 50 | server localhost:11221; 51 | server localhost:11222; 52 | server localhost:11223; 53 | server localhost:11224; 54 | server localhost:11225; 55 | server localhost:11226; 56 | server localhost:11227; 57 | server localhost:11228; 58 | server localhost:11229; 59 | server localhost:11230; 60 | 61 | hash $uri; 62 | hash_again 64; 63 | } 64 | 65 | server { 66 | listen localhost:8081; 67 | server_name localhost; 68 | 69 | #access_log logs/host.access.log main; 70 | 71 | location / { 72 | set $memcached_key $uri; 73 | memcached_pass backend; 74 | memcached_next_upstream error timeout; 75 | } 76 | 77 | # redirect server error pages to the static page /50x.html 78 | # 79 | error_page 500 502 503 504 /50x.html; 80 | location = /50x.html { 81 | root html; 82 | } 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /t/nginx/conf/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | #user nobody; 3 | worker_processes 1; 4 | 5 | #error_log logs/error.log; 6 | error_log logs/error.log debug; 7 | #error_log logs/error.log notice; 8 | #error_log logs/error.log info; 9 | 10 | #pid logs/nginx.pid; 11 | 12 | 13 | events { 14 | worker_connections 1024; 15 | } 16 | 17 | 18 | http { 19 | include mime.types; 20 | default_type application/octet-stream; 21 | 22 | #log_format main '$remote_addr - $remote_user [$time_local] $request ' 23 | # '"$status" $body_bytes_sent "$http_referer" ' 24 | # '"$http_user_agent" "$http_x_forwarded_for"'; 25 | 26 | #access_log logs/access.log main; 27 | 28 | sendfile on; 29 | #tcp_nopush on; 30 | 31 | #keepalive_timeout 0; 32 | keepalive_timeout 65; 33 | 34 | #gzip on; 35 | 36 | upstream backend { 37 | # active 38 | server localhost:11211; 39 | server localhost:11212; 40 | server localhost:11213; 41 | server localhost:11214; 42 | server localhost:11215; 43 | 44 | # dead 45 | server localhost:11216; 46 | server localhost:11217; 47 | server localhost:11218; 48 | server localhost:11219; 49 | server localhost:11220; 50 | server localhost:11221; 51 | server localhost:11222; 52 | server localhost:11223; 53 | server localhost:11224; 54 | server localhost:11225; 55 | server localhost:11226; 56 | server localhost:11227; 57 | server localhost:11228; 58 | server localhost:11229; 59 | server localhost:11230; 60 | 61 | hash $uri; 62 | hash_again 64; 63 | } 64 | 65 | server { 66 | listen localhost:8081; 67 | server_name localhost; 68 | 69 | #access_log logs/host.access.log main; 70 | 71 | location / { 72 | set $memcached_key $uri; 73 | memcached_pass backend; 74 | memcached_next_upstream error timeout; 75 | } 76 | 77 | # redirect server error pages to the static page /50x.html 78 | # 79 | error_page 500 502 503 504 /50x.html; 80 | location = /50x.html { 81 | root html; 82 | } 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /t/restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pkill nginx 4 | cp nginx.conf ./nginx/conf/ 5 | ./nginx/sbin/nginx 6 | 7 | killall memcached 8 | memcached -d -l 127.0.0.1 -p 11211 9 | memcached -d -l 127.0.0.1 -p 11212 10 | memcached -d -l 127.0.0.1 -p 11213 11 | memcached -d -l 127.0.0.1 -p 11214 12 | memcached -d -l 127.0.0.1 -p 11215 13 | --------------------------------------------------------------------------------