Mercurial > hg > nginx
annotate src/http/modules/ngx_http_random_index_module.c @ 4207:4fc91bae6f83
Better recheck of dead upstream servers.
Previously nginx used to mark backend again as live as soon as fail_timeout
passes (10s by default) since last failure. On the other hand, detecting
dead backend takes up to 60s (proxy_connect_timeout) in typical situation
"backend is down and doesn't respond to any packets". This resulted in
suboptimal behaviour in the above situation (up to 23% of requests were
directed to dead backend with default settings).
More detailed description of the problem may be found here (in Russian):
http://mailman.nginx.org/pipermail/nginx-ru/2011-August/042172.html
Fix is to only allow one request after fail_timeout passes, and
mark backend as "live" only if this request succeeds.
Note that with new code backend will not be marked "live" unless "check"
request is completed, and this may take a while in some specific workloads
(e.g. streaming). This is believed to be acceptable.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Wed, 12 Oct 2011 14:22:48 +0000 |
parents | 84905c7b2aa7 |
children | d620f497c50f |
rev | line source |
---|---|
2235 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_http.h> | |
10 | |
11 | |
12 typedef struct { | |
13 ngx_flag_t enable; | |
14 } ngx_http_random_index_loc_conf_t; | |
15 | |
16 | |
17 #define NGX_HTTP_RANDOM_INDEX_PREALLOCATE 50 | |
18 | |
19 | |
20 static ngx_int_t ngx_http_random_index_error(ngx_http_request_t *r, | |
21 ngx_dir_t *dir, ngx_str_t *name); | |
2266
6223d5a9e87f
back out $random_index variable
Igor Sysoev <igor@sysoev.ru>
parents:
2258
diff
changeset
|
22 static ngx_int_t ngx_http_random_index_init(ngx_conf_t *cf); |
2235 | 23 static void *ngx_http_random_index_create_loc_conf(ngx_conf_t *cf); |
24 static char *ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf, | |
25 void *parent, void *child); | |
26 | |
27 | |
28 static ngx_command_t ngx_http_random_index_commands[] = { | |
29 | |
30 { ngx_string("random_index"), | |
31 NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
32 ngx_conf_set_flag_slot, | |
33 NGX_HTTP_LOC_CONF_OFFSET, | |
34 offsetof(ngx_http_random_index_loc_conf_t, enable), | |
35 NULL }, | |
36 | |
37 ngx_null_command | |
38 }; | |
39 | |
40 | |
41 static ngx_http_module_t ngx_http_random_index_module_ctx = { | |
2266
6223d5a9e87f
back out $random_index variable
Igor Sysoev <igor@sysoev.ru>
parents:
2258
diff
changeset
|
42 NULL, /* preconfiguration */ |
2235 | 43 ngx_http_random_index_init, /* postconfiguration */ |
44 | |
45 NULL, /* create main configuration */ | |
46 NULL, /* init main configuration */ | |
47 | |
48 NULL, /* create server configuration */ | |
49 NULL, /* merge server configuration */ | |
50 | |
51 ngx_http_random_index_create_loc_conf, /* create location configration */ | |
52 ngx_http_random_index_merge_loc_conf /* merge location configration */ | |
53 }; | |
54 | |
55 | |
56 ngx_module_t ngx_http_random_index_module = { | |
57 NGX_MODULE_V1, | |
58 &ngx_http_random_index_module_ctx, /* module context */ | |
59 ngx_http_random_index_commands, /* module directives */ | |
60 NGX_HTTP_MODULE, /* module type */ | |
61 NULL, /* init master */ | |
62 NULL, /* init module */ | |
63 NULL, /* init process */ | |
64 NULL, /* init thread */ | |
65 NULL, /* exit thread */ | |
66 NULL, /* exit process */ | |
67 NULL, /* exit master */ | |
68 NGX_MODULE_V1_PADDING | |
69 }; | |
70 | |
71 | |
72 static ngx_int_t | |
73 ngx_http_random_index_handler(ngx_http_request_t *r) | |
74 { | |
75 u_char *last, *filename; | |
76 size_t len, allocated, root; | |
77 ngx_err_t err; | |
78 ngx_int_t rc; | |
79 ngx_str_t path, uri, *name; | |
80 ngx_dir_t dir; | |
81 ngx_uint_t n, level; | |
82 ngx_array_t names; | |
83 ngx_http_random_index_loc_conf_t *rlcf; | |
84 | |
85 if (r->uri.data[r->uri.len - 1] != '/') { | |
86 return NGX_DECLINED; | |
87 } | |
88 | |
89 if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) { | |
90 return NGX_DECLINED; | |
91 } | |
92 | |
93 rlcf = ngx_http_get_module_loc_conf(r, ngx_http_random_index_module); | |
94 | |
95 if (!rlcf->enable) { | |
96 return NGX_DECLINED; | |
97 } | |
98 | |
99 #if (NGX_HAVE_D_TYPE) | |
100 len = NGX_DIR_MASK_LEN; | |
101 #else | |
102 len = NGX_HTTP_RANDOM_INDEX_PREALLOCATE; | |
103 #endif | |
104 | |
105 last = ngx_http_map_uri_to_path(r, &path, &root, len); | |
106 if (last == NULL) { | |
107 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
108 } | |
109 | |
110 allocated = path.len; | |
111 | |
112 path.len = last - path.data - 1; | |
113 path.data[path.len] = '\0'; | |
114 | |
115 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
116 "http random index: \"%s\"", path.data); | |
117 | |
118 if (ngx_open_dir(&path, &dir) == NGX_ERROR) { | |
119 err = ngx_errno; | |
120 | |
121 if (err == NGX_ENOENT | |
122 || err == NGX_ENOTDIR | |
123 || err == NGX_ENAMETOOLONG) | |
124 { | |
125 level = NGX_LOG_ERR; | |
126 rc = NGX_HTTP_NOT_FOUND; | |
127 | |
128 } else if (err == NGX_EACCES) { | |
129 level = NGX_LOG_ERR; | |
130 rc = NGX_HTTP_FORBIDDEN; | |
131 | |
132 } else { | |
133 level = NGX_LOG_CRIT; | |
134 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
135 } | |
136 | |
137 ngx_log_error(level, r->connection->log, err, | |
138 ngx_open_dir_n " \"%s\" failed", path.data); | |
139 | |
140 return rc; | |
141 } | |
142 | |
143 if (ngx_array_init(&names, r->pool, 32, sizeof(ngx_str_t)) != NGX_OK) { | |
144 return ngx_http_random_index_error(r, &dir, &path); | |
145 } | |
146 | |
147 filename = path.data; | |
148 filename[path.len] = '/'; | |
149 | |
150 for ( ;; ) { | |
151 ngx_set_errno(0); | |
152 | |
153 if (ngx_read_dir(&dir) == NGX_ERROR) { | |
154 err = ngx_errno; | |
155 | |
156 if (err != NGX_ENOMOREFILES) { | |
157 ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, | |
158 ngx_read_dir_n " \"%V\" failed", &path); | |
159 return ngx_http_random_index_error(r, &dir, &path); | |
160 } | |
161 | |
162 break; | |
163 } | |
164 | |
165 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
166 "http random index file: \"%s\"", ngx_de_name(&dir)); | |
167 | |
168 if (ngx_de_name(&dir)[0] == '.') { | |
169 continue; | |
170 } | |
171 | |
172 len = ngx_de_namelen(&dir); | |
173 | |
3203
a56cccd588e6
update r3201: ngx_http_random_index_module should behave consistently
Igor Sysoev <igor@sysoev.ru>
parents:
3200
diff
changeset
|
174 if (dir.type == 0 || ngx_de_is_link(&dir)) { |
2235 | 175 |
176 /* 1 byte for '/' and 1 byte for terminating '\0' */ | |
177 | |
178 if (path.len + 1 + len + 1 > allocated) { | |
179 allocated = path.len + 1 + len + 1 | |
180 + NGX_HTTP_RANDOM_INDEX_PREALLOCATE; | |
181 | |
182 filename = ngx_pnalloc(r->pool, allocated); | |
183 if (filename == NULL) { | |
184 return ngx_http_random_index_error(r, &dir, &path); | |
185 } | |
186 | |
187 last = ngx_cpystrn(filename, path.data, path.len + 1); | |
188 *last++ = '/'; | |
189 } | |
190 | |
191 ngx_cpystrn(last, ngx_de_name(&dir), len + 1); | |
192 | |
193 if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) { | |
194 err = ngx_errno; | |
195 | |
196 if (err != NGX_ENOENT) { | |
197 ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, | |
198 ngx_de_info_n " \"%s\" failed", filename); | |
199 return ngx_http_random_index_error(r, &dir, &path); | |
200 } | |
201 | |
202 if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) { | |
203 ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, | |
204 ngx_de_link_info_n " \"%s\" failed", | |
205 filename); | |
206 return ngx_http_random_index_error(r, &dir, &path); | |
207 } | |
208 } | |
209 } | |
210 | |
211 if (!ngx_de_is_file(&dir)) { | |
212 continue; | |
213 } | |
214 | |
215 name = ngx_array_push(&names); | |
216 if (name == NULL) { | |
217 return ngx_http_random_index_error(r, &dir, &path); | |
218 } | |
219 | |
220 name->len = len; | |
221 | |
222 name->data = ngx_pnalloc(r->pool, len); | |
223 if (name->data == NULL) { | |
224 return ngx_http_random_index_error(r, &dir, &path); | |
225 } | |
226 | |
227 ngx_memcpy(name->data, ngx_de_name(&dir), len); | |
228 } | |
229 | |
230 if (ngx_close_dir(&dir) == NGX_ERROR) { | |
231 ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, | |
232 ngx_close_dir_n " \"%s\" failed", &path); | |
233 } | |
234 | |
235 n = names.nelts; | |
236 | |
237 if (n == 0) { | |
238 return NGX_DECLINED; | |
239 } | |
240 | |
241 name = names.elts; | |
242 | |
243 n = (ngx_uint_t) (((uint64_t) ngx_random() * n) / 0x80000000); | |
244 | |
245 uri.len = r->uri.len + name[n].len; | |
246 | |
247 uri.data = ngx_pnalloc(r->pool, uri.len); | |
248 if (uri.data == NULL) { | |
249 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
250 } | |
251 | |
252 last = ngx_copy(uri.data, r->uri.data, r->uri.len); | |
253 ngx_memcpy(last, name[n].data, name[n].len); | |
254 | |
255 return ngx_http_internal_redirect(r, &uri, &r->args); | |
256 } | |
257 | |
258 | |
259 static ngx_int_t | |
260 ngx_http_random_index_error(ngx_http_request_t *r, ngx_dir_t *dir, | |
261 ngx_str_t *name) | |
262 { | |
263 if (ngx_close_dir(dir) == NGX_ERROR) { | |
264 ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, | |
265 ngx_close_dir_n " \"%V\" failed", name); | |
266 } | |
267 | |
268 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
269 } | |
270 | |
271 | |
272 static void * | |
273 ngx_http_random_index_create_loc_conf(ngx_conf_t *cf) | |
274 { | |
275 ngx_http_random_index_loc_conf_t *conf; | |
276 | |
277 conf = ngx_palloc(cf->pool, sizeof(ngx_http_random_index_loc_conf_t)); | |
278 if (conf == NULL) { | |
2912
c7d57b539248
return NULL instead of NGX_CONF_ERROR on a create conf failure
Igor Sysoev <igor@sysoev.ru>
parents:
2721
diff
changeset
|
279 return NULL; |
2235 | 280 } |
281 | |
282 conf->enable = NGX_CONF_UNSET; | |
283 | |
284 return conf; | |
285 } | |
286 | |
287 | |
288 static char * | |
289 ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) | |
290 { | |
291 ngx_http_random_index_loc_conf_t *prev = parent; | |
292 ngx_http_random_index_loc_conf_t *conf = child; | |
293 | |
294 ngx_conf_merge_value(conf->enable, prev->enable, 0); | |
295 | |
296 return NGX_CONF_OK; | |
297 } | |
298 | |
299 | |
300 static ngx_int_t | |
301 ngx_http_random_index_init(ngx_conf_t *cf) | |
302 { | |
303 ngx_http_handler_pt *h; | |
304 ngx_http_core_main_conf_t *cmcf; | |
305 | |
306 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); | |
307 | |
308 h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); | |
309 if (h == NULL) { | |
310 return NGX_ERROR; | |
311 } | |
312 | |
313 *h = ngx_http_random_index_handler; | |
314 | |
315 return NGX_OK; | |
316 } |