AnyEvent-Task
view release on metacpan or search on metacpan
Note that since the protocol between the client and the worker process
is currently JSON-based, all arguments and return values must be
serializable to JSON. This includes most perl scalars like strings, a
limited range of numerical types, and hash/list constructs with no
cyclical references.
Because there isn't any way for the callback to indicate the context it
desires, interface subs are always called in scalar context.
A future backwards compatible RPC protocol may use Sereal. Although it's
inefficient you can already serialise an object with Sereal manually,
send the resulting string over the existing protocol, and then
deserialise it in the worker.
LOGGING
Because workers run in a separate process, they can't directly use
logging contexts in the client process. That is why this module is
integrated with Log::Defer.
A Log::Defer object is created on demand in the worker process. Once the
worker is done an operation, any messages in the object will be
extracted and sent back to the client. The client then merges this into
its main Log::Defer object that was passed in when creating the
checkout.
In your server code, use AnyEvent::Task::Logger. It exports the function
"logger" which returns a Log::Defer object:
use AnyEvent::Task::Server;
use AnyEvent::Task::Logger;
AnyEvent::Task::Server->new(
name => 'sleeper',
listen => ['unix/', '/tmp/anyevent-task.socket'],
interface => sub {
logger->info('about to compute some operation');
{
my $timer = logger->timer('computing some operation');
select undef,undef,undef, 1; ## sleep for 1 second
}
},
)->run;
Note: Portable server code should never call "sleep" because on some
systems it will interfere with the recoverable worker timeout feature
implemented with "SIGALRM".
In your client code, pass a Log::Defer object in when you create a
checkout:
use AnyEvent::Task::Client;
use Log::Defer;
my $client = AnyEvent::Task::Client->new(
connect => ['unix/', '/tmp/anyevent-task.socket'],
);
my $log_defer_object = Log::Defer->new(sub {
my $msg = shift;
use Data::Dumper; ## or whatever
print Dumper($msg);
});
$log_defer_object->info('going to compute some operation in a worker');
my $checkout = $client->checkout(log_defer_object => $log_defer_object);
my $cv = AE::cv;
$checkout->(sub {
$log_defer_object->info('finished some operation');
$cv->send;
});
$cv->recv;
When run, the above client will print something like this:
$VAR1 = {
'start' => '1363232705.96839',
'end' => '1.027309',
'logs' => [
[
'0.000179',
30,
'going to compute some operation in a worker'
],
[
'0.023881061050415',
30,
'about to compute some operation'
],
[
'1.025965',
30,
'finished some operation'
]
],
'timers' => {
'computing some operation' => [
'0.024089061050415',
'1.02470206105041'
]
}
};
ERROR HANDLING
In a synchronous program, if you expected some operation to throw an
exception you might wrap it in "eval" like this:
my $crypted;
eval {
$crypted = hash('secret');
};
if ($@) {
say "hash failed: $@";
} else {
say "hashed password is $crypted";
( run in 0.672 second using v1.01-cache-2.11-cpan-39bf76dae61 )