--- cvsroot/AnyEvent-SNMP/SNMP.pm 2009/08/02 14:24:23 1.7 +++ cvsroot/AnyEvent-SNMP/SNMP.pm 2019/10/09 18:22:59 1.14 @@ -25,20 +25,24 @@ =head1 DESCRIPTION This module implements an alternative "event dispatcher" for Net::SNMP, -using AnyEvent as a backend. - -This integrates Net::SNMP into AnyEvent: You can make non-blocking -Net::SNMP calls and as long as other parts of your program also use -AnyEvent (or some event loop supported by AnyEvent), they will run in -parallel. +using AnyEvent as a backend. This integrates Net::SNMP into AnyEvent. That +means you can make non-blocking Net::SNMP calls and as long as other +parts of your program also use AnyEvent (or some event loop supported by +AnyEvent), they will run in parallel. Also, the Net::SNMP scheduler is very inefficient with respect to both CPU and memory usage. Most AnyEvent backends (including the pure-perl backend) fare much better than the Net::SNMP dispatcher. -A potential disadvantage is that replacing the dispatcher is not at all -a documented thing to do, so future changes in Net::SNP might break this -module (or the many similar ones). +Another major added feature of this module over Net::SNMP is automatic +rate-adjustments: Net::SNMP is so slow that firing a few thousand +requests can cause many timeouts simply because Net::SNMP cannot process +the replies in time. This module automatically adapts the send rate to +avoid false timeouts caused by slow reply processing. + +A potential disadvantage of this module is that replacing the dispatcher +is not at all a documented thing to do, so future changes in Net::SNMP +might break this module (or the many similar ones). This module does not export anything and does not require you to do anything special apart from loading it I will not automatically use the -C and make -C make use of the extra paralellity, call +extra request slots. To increase C<$MAX_OUTSTANDING> and make +C make use of the extra parallelity, call C with the new value, e.g.: AnyEvent::SNMP::set_max_outstanding 500; @@ -101,10 +105,10 @@ it increases $MAX_OUTSTANDING. This has the result of adjusting the number of outstanding requests so that -the recv queue is between the minimum and maximu, usually. +the recv queue is between the minimum and maximum, usually. This algorithm works reasonably well as long as the responses, response -latencies and processing times are the same size per packet on average. +latencies and processing times are the same per packet on average. =back @@ -133,13 +137,16 @@ package AnyEvent::SNMP; -no warnings; -use strict qw(subs vars); +use common::sense; # it is possible to do this without loading # Net::SNMP::Dispatcher, but much more awkward. use Net::SNMP::Dispatcher; +# we could inherit fro Net:SNMP::Dispatcher, but since this is undocumented, +# I'd rather see it die (and reported) than silenty and subtly fail. +*msg_handle_alloc = \&Net::SNMP::Dispatcher::msg_handle_alloc; + sub Net::SNMP::Dispatcher::instance { AnyEvent::SNMP:: } @@ -147,17 +154,14 @@ use Net::SNMP (); use AnyEvent (); -our $VERSION = '0.2'; +our $VERSION = '6.02'; $Net::SNMP::DISPATCHER = instance Net::SNMP::Dispatcher; our $MESSAGE_PROCESSING = $Net::SNMP::Dispatcher::MESSAGE_PROCESSING; -# avoid the method call -my $timer = sub { shift->timer (@_) }; -AnyEvent::post_detect { $timer = AnyEvent->can ("timer") }; - our $BUSY; +our $DONE; # finished all jobs our @TRANSPORT; # fileno => [count, watcher] our @QUEUE; our $MAX_OUTSTANDING = 50; @@ -190,10 +194,10 @@ # A crude attempt to recover from temporary failures. if ($retries-- > 0 && ($!{EAGAIN} || $!{EWOULDBLOCK} || $!{ENOSPC})) { - my $retry_w; $retry_w = AnyEvent->$timer (after => $pdu->timeout, cb => sub { + my $retry_w; $retry_w = AE::timer $pdu->timeout, 0, sub { undef $retry_w; _send_pdu ($pdu, $retries); - }); + }; } else { --$BUSY; kick_job; @@ -211,7 +215,7 @@ # register the transport unless ($TRANSPORT[$fileno][0]++) { - $TRANSPORT[$fileno][1] = AnyEvent->io (fh => $transport->socket, poll => 'r', cb => sub { + $TRANSPORT[$fileno][1] = AE::io $transport->socket, 0, sub { for my $count (1..$MAX_RECVQUEUE) { # handle up to this many requests in one go # Create a new Message object to receive the response my ($msg, $error) = Net::SNMP::Message->new (-transport => $transport); @@ -255,7 +259,11 @@ $msg->error ($MESSAGE_PROCESSING->error) if $MESSAGE_PROCESSING->error; # Notify the command generator to process the response. - $msg->process_response_pdu; + # Net::SNMP calls process_response_pdu, which simply calls callback_execute, + # but some errors cause $msg to be of type Net::SNMP::Message, not Net::SMMP::PDU, + # so we call the underlying callback_execute method which exists on both and + # seems to do the right thing. + $msg->callback_execute; # Cancel the timeout. my $rtimeout_w = $msg->timeout_id; @@ -276,11 +284,11 @@ # replies in one iteration, so assume we are overloaded # and reduce the amount of parallelity. $MAX_OUTSTANDING = (int $MAX_OUTSTANDING * 0.95) || 1; - }); + }; } $msg->timeout_id (\(my $rtimeout_w = - AnyEvent->$timer (after => $pdu->timeout, cb => sub { + AE::timer $pdu->timeout, 0, sub { my $rtimeout_w = $msg->timeout_id; if ($$rtimeout_w) { undef $$rtimeout_w; @@ -298,7 +306,7 @@ kick_job; } }) - )); + ); } else { --$BUSY; kick_job; @@ -311,9 +319,10 @@ or last; ++$BUSY; - _send_pdu $pdu, $pdu->retries; } + + $DONE and $DONE->() unless $BUSY; } sub send_pdu($$$) { @@ -323,12 +332,12 @@ # but apparently it is not a very sensible feature. if ($delay > 0) { ++$BUSY; - my $delay_w; $delay_w = AnyEvent->$timer (after => $delay, cb => sub { + my $delay_w; $delay_w = AE::timer $delay, 0, sub { undef $delay_w; - --$BUSY; push @QUEUE, $pdu; + --$BUSY; kick_job; - }); + }; return 1; } @@ -338,12 +347,20 @@ 1 } -sub activate($) { - AnyEvent->one_event while $BUSY; +sub loop($) { + while ($BUSY) { + $DONE = AE::cv; + $DONE->recv; + undef $DONE; + } } +*activate = \&loop; # 5.x compatibility? +*listen = \&loop; # 5.x compatibility? + sub one_event($) { - AnyEvent->one_event; + # should not ever be used + AnyEvent->one_event; #d# todo } sub set_max_outstanding($) { @@ -351,6 +368,15 @@ kick_job; } +# not provided yet: +# schedule # apparently only used by Net::SNMP::Dispatcher itself +# register # apparently only used by Net::SNMP::Dispatcher itself +# deregister # apparently only used by Net::SNMP::Dispatcher itself +# cancel # apparently only used by Net::SNMP::Dispatcher itself +# return_response_pdu # apparently not used at all? +# error # only used by Net::SNMP::Dispatcher itself? +# debug # only used by Net::SNMP::Dispatcher itself? + =head1 SEE ALSO L, L, L, L.