… | |
… | |
23 | my @result = $cv->wait; |
23 | my @result = $cv->wait; |
24 | |
24 | |
25 | =head1 DESCRIPTION |
25 | =head1 DESCRIPTION |
26 | |
26 | |
27 | This module implements an alternative "event dispatcher" for Net::SNMP, |
27 | This module implements an alternative "event dispatcher" for Net::SNMP, |
28 | using AnyEvent as a backend. |
28 | using AnyEvent as a backend. This integrates Net::SNMP into AnyEvent. That |
29 | |
29 | means you can make non-blocking Net::SNMP calls and as long as other |
30 | This integrates Net::SNMP into AnyEvent: You can make non-blocking |
30 | parts of your program also use AnyEvent (or some event loop supported by |
31 | Net::SNMP calls and as long as other parts of your program also use |
31 | AnyEvent), they will run in parallel. |
32 | AnyEvent (or some event loop supported by AnyEvent), they will run in |
|
|
33 | parallel. |
|
|
34 | |
32 | |
35 | Also, the Net::SNMP scheduler is very inefficient with respect to both CPU |
33 | Also, the Net::SNMP scheduler is very inefficient with respect to both CPU |
36 | and memory usage. Most AnyEvent backends (including the pure-perl backend) |
34 | and memory usage. Most AnyEvent backends (including the pure-perl backend) |
37 | fare much better than the Net::SNMP dispatcher. |
35 | fare much better than the Net::SNMP dispatcher. |
38 | |
36 | |
|
|
37 | Another major added fetaure of this module over Net::SNMP is automatic |
|
|
38 | rate-adjustments: Net::SNMP is so slow that firing a few thousand |
|
|
39 | requests can cause many timeouts simply because Net::SNMP cannot process |
|
|
40 | the replies in time. This module automatically adapts the send rate to |
|
|
41 | avoid false timeouts caused by slow reply processing. |
|
|
42 | |
39 | A potential disadvantage is that replacing the dispatcher is not at all |
43 | A potential disadvantage of this module is that replacing the dispatcher |
40 | a documented thing to do, so future changes in Net::SNP might break this |
44 | is not at all a documented thing to do, so future changes in Net::SNP |
41 | module (or the many similar ones). |
45 | might break this module (or the many similar ones). |
42 | |
46 | |
43 | This module does not export anything and does not require you to do |
47 | This module does not export anything and does not require you to do |
44 | anything special apart from loading it I<before doing any non-blocking |
48 | anything special apart from loading it I<before doing any non-blocking |
45 | requests with Net::SNMP>. It is recommended but not required to load this |
49 | requests with Net::SNMP>. It is recommended but not required to load this |
46 | module before C<Net::SNMP>. |
50 | module before C<Net::SNMP>. |
… | |
… | |
71 | |
75 | |
72 | Unfortunately, this number depends not only on processing speed and load |
76 | Unfortunately, this number depends not only on processing speed and load |
73 | of the machine running Net::SNMP, but also on the network latency and the |
77 | of the machine running Net::SNMP, but also on the network latency and the |
74 | speed of your SNMP agents. |
78 | speed of your SNMP agents. |
75 | |
79 | |
76 | AnyEvent::SNMP tries to dynamically adjust this number dynamically upwards |
80 | AnyEvent::SNMP tries to dynamically adjust this number upwards and |
77 | and downwards. |
81 | downwards. |
78 | |
82 | |
79 | Increasing C<$MAX_OUTSTANDING> will not automatically use the |
83 | Increasing C<$MAX_OUTSTANDING> will not automatically use the |
80 | extra request slots. To increase C<$MAX_OUTSTANDING> and make |
84 | extra request slots. To increase C<$MAX_OUTSTANDING> and make |
81 | C<AnyEvent::SNMP> make use of the extra paralellity, call |
85 | C<AnyEvent::SNMP> make use of the extra paralellity, call |
82 | C<AnyEvent::SNMP::set_max_outstanding> with the new value, e.g.: |
86 | C<AnyEvent::SNMP::set_max_outstanding> with the new value, e.g.: |
… | |
… | |
131 | |
135 | |
132 | =cut |
136 | =cut |
133 | |
137 | |
134 | package AnyEvent::SNMP; |
138 | package AnyEvent::SNMP; |
135 | |
139 | |
136 | no warnings; |
140 | use common::sense; |
137 | use strict qw(subs vars); |
|
|
138 | |
141 | |
139 | # it is possible to do this without loading |
142 | # it is possible to do this without loading |
140 | # Net::SNMP::Dispatcher, but much more awkward. |
143 | # Net::SNMP::Dispatcher, but much more awkward. |
141 | use Net::SNMP::Dispatcher; |
144 | use Net::SNMP::Dispatcher; |
142 | |
145 | |
|
|
146 | # we could inherit fro Net:SNMP::Dispatcher, but since this is undocumented, |
|
|
147 | # I'd rather see it die (and reported) than silenty and subtly fail. |
|
|
148 | *msg_handle_alloc = \&Net::SNMP::Dispatcher::msg_handle_alloc; |
|
|
149 | |
143 | sub Net::SNMP::Dispatcher::instance { |
150 | sub Net::SNMP::Dispatcher::instance { |
144 | AnyEvent::SNMP:: |
151 | AnyEvent::SNMP:: |
145 | } |
152 | } |
146 | |
153 | |
147 | use Net::SNMP (); |
154 | use Net::SNMP (); |
148 | use AnyEvent (); |
155 | use AnyEvent (); |
149 | |
156 | |
150 | our $VERSION = '1.0'; |
157 | our $VERSION = '6.0'; |
151 | |
158 | |
152 | $Net::SNMP::DISPATCHER = instance Net::SNMP::Dispatcher; |
159 | $Net::SNMP::DISPATCHER = instance Net::SNMP::Dispatcher; |
153 | |
160 | |
154 | our $MESSAGE_PROCESSING = $Net::SNMP::Dispatcher::MESSAGE_PROCESSING; |
161 | our $MESSAGE_PROCESSING = $Net::SNMP::Dispatcher::MESSAGE_PROCESSING; |
155 | |
162 | |
… | |
… | |
159 | our @QUEUE; |
166 | our @QUEUE; |
160 | our $MAX_OUTSTANDING = 50; |
167 | our $MAX_OUTSTANDING = 50; |
161 | our $MIN_RECVQUEUE = 8; |
168 | our $MIN_RECVQUEUE = 8; |
162 | our $MAX_RECVQUEUE = 64; |
169 | our $MAX_RECVQUEUE = 64; |
163 | |
170 | |
164 | sub kick_job; # also --$BUSY |
171 | sub kick_job; |
165 | |
172 | |
166 | sub _send_pdu { |
173 | sub _send_pdu { |
167 | my ($pdu, $retries) = @_; |
174 | my ($pdu, $retries) = @_; |
168 | |
175 | |
169 | # mostly copied from Net::SNMP::Dispatch |
176 | # mostly copied from Net::SNMP::Dispatch |
… | |
… | |
171 | # Pass the PDU to Message Processing so that it can |
178 | # Pass the PDU to Message Processing so that it can |
172 | # create the new outgoing message. |
179 | # create the new outgoing message. |
173 | my $msg = $MESSAGE_PROCESSING->prepare_outgoing_msg ($pdu); |
180 | my $msg = $MESSAGE_PROCESSING->prepare_outgoing_msg ($pdu); |
174 | |
181 | |
175 | if (!defined $msg) { |
182 | if (!defined $msg) { |
|
|
183 | --$BUSY; |
176 | kick_job; |
184 | kick_job; |
177 | # Inform the command generator about the Message Processing error. |
185 | # Inform the command generator about the Message Processing error. |
178 | $pdu->status_information ($MESSAGE_PROCESSING->error); |
186 | $pdu->status_information ($MESSAGE_PROCESSING->error); |
179 | return; |
187 | return; |
180 | } |
188 | } |
… | |
… | |
189 | my $retry_w; $retry_w = AE::timer $pdu->timeout, 0, sub { |
197 | my $retry_w; $retry_w = AE::timer $pdu->timeout, 0, sub { |
190 | undef $retry_w; |
198 | undef $retry_w; |
191 | _send_pdu ($pdu, $retries); |
199 | _send_pdu ($pdu, $retries); |
192 | }; |
200 | }; |
193 | } else { |
201 | } else { |
|
|
202 | --$BUSY; |
194 | kick_job; |
203 | kick_job; |
195 | } |
204 | } |
196 | |
205 | |
197 | # Inform the command generator about the send() error. |
206 | # Inform the command generator about the send() error. |
198 | $pdu->status_information ($msg->error); |
207 | $pdu->status_information ($msg->error); |
… | |
… | |
255 | # Cancel the timeout. |
264 | # Cancel the timeout. |
256 | my $rtimeout_w = $msg->timeout_id; |
265 | my $rtimeout_w = $msg->timeout_id; |
257 | if ($$rtimeout_w) { |
266 | if ($$rtimeout_w) { |
258 | undef $$rtimeout_w; |
267 | undef $$rtimeout_w; |
259 | |
268 | |
|
|
269 | --$BUSY; |
260 | kick_job; |
270 | kick_job; |
261 | |
271 | |
262 | unless (--$TRANSPORT[$fileno][0]) { |
272 | unless (--$TRANSPORT[$fileno][0]) { |
263 | delete $TRANSPORT[$fileno]; |
273 | delete $TRANSPORT[$fileno]; |
264 | return; |
274 | return; |
… | |
… | |
286 | _send_pdu ($pdu, $retries); |
296 | _send_pdu ($pdu, $retries); |
287 | } else { |
297 | } else { |
288 | $MESSAGE_PROCESSING->msg_handle_delete ($pdu->msg_id); |
298 | $MESSAGE_PROCESSING->msg_handle_delete ($pdu->msg_id); |
289 | $pdu->status_information ("No response from remote host '%s'", $pdu->hostname); |
299 | $pdu->status_information ("No response from remote host '%s'", $pdu->hostname); |
290 | |
300 | |
|
|
301 | --$BUSY; |
291 | kick_job; |
302 | kick_job; |
292 | } |
303 | } |
293 | }) |
304 | }) |
294 | ); |
305 | ); |
295 | } else { |
306 | } else { |
|
|
307 | --$BUSY; |
296 | kick_job; |
308 | kick_job; |
297 | } |
309 | } |
298 | } |
310 | } |
299 | |
311 | |
300 | sub kick_job { |
312 | sub kick_job { |
301 | --$BUSY; |
|
|
302 | |
|
|
303 | while ($BUSY < $MAX_OUTSTANDING) { |
313 | while ($BUSY < $MAX_OUTSTANDING) { |
304 | my $pdu = shift @QUEUE |
314 | my $pdu = shift @QUEUE |
305 | or last; |
315 | or last; |
306 | |
316 | |
307 | ++$BUSY; |
317 | ++$BUSY; |
308 | |
|
|
309 | _send_pdu $pdu, $pdu->retries; |
318 | _send_pdu $pdu, $pdu->retries; |
310 | } |
319 | } |
311 | |
320 | |
312 | $DONE and $DONE->() unless $BUSY; |
321 | $DONE and $DONE->() unless $BUSY; |
313 | } |
322 | } |
… | |
… | |
320 | if ($delay > 0) { |
329 | if ($delay > 0) { |
321 | ++$BUSY; |
330 | ++$BUSY; |
322 | my $delay_w; $delay_w = AE::timer $delay, 0, sub { |
331 | my $delay_w; $delay_w = AE::timer $delay, 0, sub { |
323 | undef $delay_w; |
332 | undef $delay_w; |
324 | push @QUEUE, $pdu; |
333 | push @QUEUE, $pdu; |
|
|
334 | --$BUSY; |
325 | kick_job; |
335 | kick_job; |
326 | }; |
336 | }; |
327 | return 1; |
337 | return 1; |
328 | } |
338 | } |
329 | |
339 | |
… | |
… | |
331 | kick_job; |
341 | kick_job; |
332 | |
342 | |
333 | 1 |
343 | 1 |
334 | } |
344 | } |
335 | |
345 | |
336 | sub activate($) { |
346 | sub loop($) { |
337 | while ($BUSY) { |
347 | while ($BUSY) { |
338 | $DONE = AE::cv; |
348 | $DONE = AE::cv; |
339 | $DONE->recv; |
349 | $DONE->recv; |
340 | undef $DONE; |
350 | undef $DONE; |
341 | } |
351 | } |
342 | } |
352 | } |
343 | |
353 | |
|
|
354 | *activate = \&loop; # 5.x compatibility? |
|
|
355 | *listen = \&loop; # 5.x compatibility? |
|
|
356 | |
344 | sub one_event($) { |
357 | sub one_event($) { |
|
|
358 | # should not ever be used |
345 | AnyEvent->one_event; #d# todo |
359 | AnyEvent->one_event; #d# todo |
346 | } |
360 | } |
347 | |
361 | |
348 | sub set_max_outstanding($) { |
362 | sub set_max_outstanding($) { |
349 | $MAX_OUTSTANDING = $_[0]; |
363 | $MAX_OUTSTANDING = $_[0]; |
350 | |
|
|
351 | ++$BUSY; # kick_job decrements $BUSY |
|
|
352 | kick_job; |
364 | kick_job; |
353 | } |
365 | } |
|
|
366 | |
|
|
367 | # not provided yet: |
|
|
368 | # schedule # apparently only used by Net::SNMP::Dispatcher itself |
|
|
369 | # register # apparently only used by Net::SNMP::Dispatcher itself |
|
|
370 | # deregister # apparently only used by Net::SNMP::Dispatcher itself |
|
|
371 | # cancel # apparently only used by Net::SNMP::Dispatcher itself |
|
|
372 | # return_response_pdu # apparently not used at all? |
|
|
373 | # error # only used by Net::SNMP::Dispatcher itself? |
|
|
374 | # debug # only used by Net::SNMP::Dispatcher itself? |
354 | |
375 | |
355 | =head1 SEE ALSO |
376 | =head1 SEE ALSO |
356 | |
377 | |
357 | L<AnyEvent>, L<Net::SNMP>, L<Net::SNMP::XS>, L<Net::SNMP::EV>. |
378 | L<AnyEvent>, L<Net::SNMP>, L<Net::SNMP::XS>, L<Net::SNMP::EV>. |
358 | |
379 | |