Mercurial > hg > nginx-tests
comparison upstream_max_conns.t @ 1041:187524328926
Tests: upstream max_conns tests.
author | Sergey Kandaurov <pluknet@nginx.com> |
---|---|
date | Fri, 23 Sep 2016 00:02:46 +0300 |
parents | |
children | 766bcbb632ee |
comparison
equal
deleted
inserted
replaced
1040:1a820a5a32ae | 1041:187524328926 |
---|---|
1 #!/usr/bin/perl | |
2 | |
3 # (C) Nginx, Inc. | |
4 | |
5 # Tests for upstream module with max_conns feature. | |
6 | |
7 ############################################################################### | |
8 | |
9 use warnings; | |
10 use strict; | |
11 | |
12 use Test::More; | |
13 | |
14 use IO::Select; | |
15 | |
16 BEGIN { use FindBin; chdir($FindBin::Bin); } | |
17 | |
18 use lib 'lib'; | |
19 use Test::Nginx qw/ :DEFAULT http_end /; | |
20 | |
21 ############################################################################### | |
22 | |
23 select STDERR; $| = 1; | |
24 select STDOUT; $| = 1; | |
25 | |
26 my $t = Test::Nginx->new() | |
27 ->has(qw/http proxy rewrite upstream_least_conn upstream_ip_hash/); | |
28 | |
29 $t->write_file_expand('nginx.conf', <<'EOF'); | |
30 | |
31 %%TEST_GLOBALS%% | |
32 | |
33 daemon off; | |
34 | |
35 events { | |
36 } | |
37 | |
38 http { | |
39 %%TEST_GLOBALS_HTTP%% | |
40 | |
41 upstream u_unlim { | |
42 server 127.0.0.1:8081 max_conns=0; | |
43 server 127.0.0.1:8082; | |
44 } | |
45 upstream u_lim { | |
46 server 127.0.0.1:8081 max_conns=3; | |
47 } | |
48 | |
49 upstream u_backup { | |
50 server 127.0.0.1:8081 max_conns=2; | |
51 server 127.0.0.1:8082 backup; | |
52 } | |
53 upstream u_backup_lim { | |
54 server 127.0.0.1:8081 max_conns=2; | |
55 server 127.0.0.1:8082 backup max_conns=3; | |
56 } | |
57 | |
58 upstream u_two { | |
59 server 127.0.0.1:8081 max_conns=1; | |
60 server 127.0.0.1:8082 max_conns=1; | |
61 } | |
62 upstream u_some { | |
63 server 127.0.0.1:8081 max_conns=1; | |
64 server 127.0.0.1:8082; | |
65 } | |
66 upstream u_many { | |
67 server 127.0.0.1:8081 max_conns=1; | |
68 server 127.0.0.1:8081 max_conns=1; | |
69 server 127.0.0.1:8082; | |
70 } | |
71 | |
72 upstream u_weight { | |
73 server 127.0.0.1:8081 weight=2 max_conns=1; | |
74 server 127.0.0.1:8082; | |
75 } | |
76 | |
77 upstream u_pnu { | |
78 # special server to force next upstream | |
79 server 127.0.0.1:8084; | |
80 | |
81 server 127.0.0.1:8081 max_conns=1; | |
82 server 127.0.0.1:8082 max_conns=2; | |
83 } | |
84 | |
85 upstream u_lc { | |
86 least_conn; | |
87 server 127.0.0.1:8081 max_conns=1; | |
88 server 127.0.0.1:8082; | |
89 } | |
90 upstream u_lc_backup { | |
91 least_conn; | |
92 server 127.0.0.1:8081 max_conns=2; | |
93 server 127.0.0.1:8082 backup; | |
94 } | |
95 upstream u_lc_backup_lim { | |
96 least_conn; | |
97 server 127.0.0.1:8081 max_conns=2; | |
98 server 127.0.0.1:8082 backup max_conns=3; | |
99 } | |
100 | |
101 upstream u_ih { | |
102 ip_hash; | |
103 server 127.0.0.1:8081 max_conns=1; | |
104 } | |
105 | |
106 server { | |
107 listen 127.0.0.1:8084; | |
108 server_name localhost; | |
109 | |
110 location / { | |
111 return 444; | |
112 } | |
113 } | |
114 | |
115 server { | |
116 listen 127.0.0.1:8080; | |
117 server_name localhost; | |
118 | |
119 proxy_http_version 1.1; | |
120 proxy_set_header Connection ""; | |
121 proxy_buffering off; | |
122 | |
123 location /u { | |
124 proxy_pass http:/$uri; | |
125 } | |
126 | |
127 location /close { | |
128 proxy_pass http://127.0.0.1:8085; | |
129 } | |
130 } | |
131 } | |
132 | |
133 EOF | |
134 | |
135 | |
136 $t->run_daemon(\&http_daemon, port(8081), port(8082), port(8085)); | |
137 $t->try_run('no upstream max_conns')->plan(14); | |
138 | |
139 $t->waitforsocket('127.0.0.1:' . port(8081)); | |
140 $t->waitforsocket('127.0.0.1:' . port(8082)); | |
141 $t->waitforsocket('127.0.0.1:' . port(8085)); | |
142 | |
143 ############################################################################### | |
144 | |
145 my @ports = my ($p1, $p2) = (port(8081), port(8082)); | |
146 | |
147 # two peers without max_conns | |
148 | |
149 is(parallel('/u_unlim?delay=0', 4), "$p1: 2, $p2: 2", 'unlimited'); | |
150 | |
151 # simple test with limited peer | |
152 | |
153 is(parallel('/u_lim', 4), "$p1: 3", 'single'); | |
154 | |
155 # reopen connection to test connection subtraction | |
156 | |
157 my @s = http_get_multi('/u_lim', 1, 1.1); | |
158 http_get('/u_lim/close'); | |
159 push @s, http_get_multi('/u_lim', 1, 1.1); | |
160 http_get('/closeall'); | |
161 | |
162 is(http_end_multi(\@s), "$p1: 2", 'conn subtraction'); | |
163 | |
164 # limited peer with backup peer | |
165 | |
166 is(peers('/u_backup', 6), "$p1 $p1 $p2 $p2 $p2 $p2", 'backup'); | |
167 | |
168 # peer and backup peer, both limited | |
169 | |
170 is(peers('/u_backup_lim', 6), "$p1 $p1 $p2 $p2 $p2 ", 'backup limited'); | |
171 | |
172 # all peers limited | |
173 | |
174 is(parallel('/u_two', 4), "$p1: 1, $p2: 1", 'all peers'); | |
175 | |
176 # subset of peers limited | |
177 | |
178 is(parallel('/u_some', 4), "$p1: 1, $p2: 3", 'some peers'); | |
179 | |
180 # ensure that peer "weight" does not affect its max_conns limit | |
181 | |
182 is(parallel('/u_weight', 4), "$p1: 1, $p2: 3", 'weight'); | |
183 | |
184 # peers with equal server value aggregate max_conns limit | |
185 | |
186 is(parallel('/u_many', 6), "$p1: 2, $p2: 4", 'equal peer'); | |
187 | |
188 # connections to peer selected with proxy_next_upstream are counted | |
189 | |
190 is(parallel('/u_pnu', 4), "$p1: 1, $p2: 2", 'proxy_next_upstream'); | |
191 | |
192 # least_conn balancer tests | |
193 | |
194 is(parallel('/u_lc', 4), "$p1: 1, $p2: 3", 'least_conn'); | |
195 is(peers('/u_lc_backup', 6), "$p1 $p1 $p2 $p2 $p2 $p2", 'least_conn backup'); | |
196 is(peers('/u_lc_backup_lim', 6), "$p1 $p1 $p2 $p2 $p2 ", | |
197 'least_conn backup limited'); | |
198 | |
199 # ip_hash balancer tests | |
200 | |
201 is(parallel('/u_ih', 4), "$p1: 1", 'ip_hash'); | |
202 | |
203 ############################################################################### | |
204 | |
205 sub peers { | |
206 my ($uri, $count) = @_; | |
207 | |
208 my @sockets = http_get_multi($uri, $count, 1.1); | |
209 http_get('/closeall'); | |
210 | |
211 join ' ', map { /X-Port: (\d+)/ && $1 } | |
212 map { http_end $_ } (@sockets); | |
213 } | |
214 | |
215 sub parallel { | |
216 my ($uri, $count) = @_; | |
217 | |
218 my @sockets = http_get_multi($uri, $count); | |
219 for (1 .. 20) { | |
220 last if IO::Select->new(@sockets)->can_read(3) == $count; | |
221 select undef, undef, undef, 0.01; | |
222 } | |
223 http_get('/closeall'); | |
224 return http_end_multi(\@sockets); | |
225 } | |
226 | |
227 sub http_get_multi { | |
228 my ($uri, $count, $wait) = @_; | |
229 my @sockets; | |
230 | |
231 for (0 .. $count - 1) { | |
232 $sockets[$_] = http_get($uri, start => 1); | |
233 IO::Select->new($sockets[$_])->can_read($wait) if $wait; | |
234 } | |
235 | |
236 return @sockets; | |
237 } | |
238 | |
239 sub http_end_multi { | |
240 my ($sockets) = @_; | |
241 my %ports; | |
242 | |
243 for my $sock (@$sockets) { | |
244 if (http_end($sock) =~ /X-Port: (\d+)/) { | |
245 $ports{$1} = 0 unless defined $ports{$1}; | |
246 $ports{$1}++; | |
247 } | |
248 close $sock; | |
249 } | |
250 | |
251 my @keys = map { my $p = $_; grep { $p == $_ } keys %ports } @ports; | |
252 return join ', ', map { $_ . ": " . $ports{$_} } @keys; | |
253 } | |
254 | |
255 ############################################################################### | |
256 | |
257 sub http_daemon { | |
258 my (@ports) = @_; | |
259 my (@socks, @clients); | |
260 | |
261 for my $port (@ports) { | |
262 my $server = IO::Socket::INET->new( | |
263 Proto => 'tcp', | |
264 LocalHost => "127.0.0.1:$port", | |
265 Listen => 42, | |
266 Reuse => 1 | |
267 ) | |
268 or die "Can't create listening socket: $!\n"; | |
269 push @socks, $server; | |
270 } | |
271 | |
272 my $sel = IO::Select->new(@socks); | |
273 my $skip = 4; | |
274 my $count = 0; | |
275 | |
276 local $SIG{PIPE} = 'IGNORE'; | |
277 | |
278 OUTER: | |
279 while (my @ready = $sel->can_read) { | |
280 foreach my $fh (@ready) { | |
281 if (grep $_ == $fh, @socks) { | |
282 my $new = $fh->accept; | |
283 $new->autoflush(1); | |
284 $sel->add($new); | |
285 $count++; | |
286 | |
287 } else { | |
288 my @busy = grep { $_->sockport() } @ready; | |
289 | |
290 # finish other handles | |
291 if ($fh->sockport() == port(8085) && @busy > 1 | |
292 && grep $_->sockport() != port(8085), | |
293 @busy) | |
294 { | |
295 next; | |
296 } | |
297 | |
298 # late events in other handles | |
299 if ($fh->sockport() == port(8085) && @busy == 1 | |
300 && $count > 1 && $skip-- > 0) | |
301 { | |
302 select undef, undef, undef, 0.1; | |
303 next OUTER; | |
304 } | |
305 | |
306 my $rv = process_socket($fh, \@clients); | |
307 if ($rv == 1) { | |
308 $sel->remove($fh); | |
309 $fh->close; | |
310 } | |
311 if ($rv == 2) { | |
312 for (@clients) { | |
313 $sel->remove($_); | |
314 $_->close; | |
315 } | |
316 $sel->remove($fh); | |
317 $fh->close; | |
318 $skip = 4; | |
319 } | |
320 $count--; | |
321 } | |
322 } | |
323 } | |
324 } | |
325 | |
326 # Returns true to close connection | |
327 | |
328 sub process_socket { | |
329 my ($client, $saved) = @_; | |
330 my $port = $client->sockport(); | |
331 | |
332 my $headers = ''; | |
333 my $uri = ''; | |
334 | |
335 while (<$client>) { | |
336 $headers .= $_; | |
337 last if (/^\x0d?\x0a?$/); | |
338 } | |
339 return 1 if $headers eq ''; | |
340 | |
341 $uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i; | |
342 return 1 if $uri eq ''; | |
343 | |
344 Test::Nginx::log_core('||', "$port: response, 200"); | |
345 print $client <<EOF; | |
346 HTTP/1.1 200 OK | |
347 X-Port: $port | |
348 | |
349 OK | |
350 EOF | |
351 | |
352 return 2 if $uri =~ /closeall/; | |
353 return 1 if $uri =~ /close/; | |
354 | |
355 push @$saved, $client; | |
356 return 0; | |
357 } | |
358 | |
359 ############################################################################### |