Perl:在(可能改变)时间延迟之后重复调用子程序

时间:2016-02-26 17:29:11

标签: perl events alarm

好的,所以我很难过。这是我似乎无法解决的问题:在调用公共API时,首先需要的是访问令牌 - 让我们调用我的方法get_access_token()。一旦我获得了访问令牌,我将使用它从公共API获取数据。现在,令牌在一定的秒数后到期,该号码随访问令牌一起提供。因此,我们的想法是设置某种计时器,通过在前一个令牌到期后再次调用get_access_token()来刷新访问令牌,这样我就可以继续从API获取数据,并始终使用有效的访问令牌。 / p>

我尝试过使用:

  • 报警
  • AnyEvent
  • 时间:: HiRes qw(setitimer ITIMER_VIRTUAL时间)

但我无法使用其中任何一项。我没有坚持任何特定的方法;我知道我可以通过简单的方式解决它,并经常检查访问令牌是否即将到期"使用time()和算术,但我想我会尝试一些更复杂的东西。显然对我来说太复杂了!

是的,我知道通常会发布一个代码,但显然我的代码都是废话,所以我真的只是在寻找一些关于如何最好地处理这个特定用例的指针。任何帮助将不胜感激。

修改

对于@ThisSuitIsBlackNot,这里是警报测试(没有进行实际的API调用)。警报触发一次,但从不再发生:

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

use Time::HiRes;

my $timeout = 7;
my $counter = 0;

my $rv = wrapper();
say $rv;

exit;


### subroutines

sub getAccessToken {
    return localtime(time());
}

sub getSomeIDs {
    my $accessToken = shift;

    say "getting IDs: timeout == $timeout";

    eval {
        local $SIG{'ALRM'} = sub { wrapper(); };
        alarm($timeout);
        while (1) {
            if ($counter == 25) { return; }
            say "[ $accessToken ] $counter";
            $counter = $counter + 1;
            alarm_resistant_sleep(1);
        }
        alarm(0);
    };
    alarm(0);

    return "counter at $counter";
}

sub wrapper {
    my $at = getAccessToken();
    return getSomeIDs($at);
}

sub alarm_resistant_sleep {
    my $end = Time::HiRes::time() + shift();
    for (;;) {
        my $delta = $end - Time::HiRes::time();
        last if $delta <= 0;
        select(undef, undef, undef, $delta);
    }
}

这是尝试使用AnyEvent。最初我没有使用封闭的东西,但读了另一篇建议这样做的帖子(仍然没有工作):

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

use AnyEvent;

my $accessToken;
my $timeout = 7;
my $cv = AE::cv;

my $counter = 0;

my $f1 = makeClosure(\&refreshAccessToken);
my $f2 = makeClosure(\&getSomeIDs);

$f1->();
$f2->();
$cv->recv();

exit;


### subroutines

sub makeClosure {
    my $sub = shift;
    return $sub;
}

sub getAccessToken {
    $accessToken = localtime(time());
}

sub refreshAccessToken {
    my $t1; $t1 = AE::timer 0, $timeout,
        sub {
            say "callback called";
            $timeout--;
            $accessToken = localtime(time());
            #getAccessToken();
            undef $t1;
        };
    say "calling refreshAccessToken()";
}

sub getSomeIDs {
    my $t2; $t2 = AE::timer 0, 1,
        sub {
            if ($counter >= 16) { undef $t2; }
            say "[ $accessToken ] $counter";
            $counter = $counter + 1;
        };
    say "getting IDs: timeout == $timeout";
}

最后,这是Time::HiRes测试。同样,这段代码经过了许多修改,试图让它工作。针对$counter测试的荒谬值只是为了看看计时器是否在任何时候被调用,而不是:

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

use Time::HiRes qw(setitimer ITIMER_VIRTUAL time);

my $accessToken;

my $counter = 0;

$SIG{VTALRM} = \&refreshAccessToken;
setitimer(ITIMER_VIRTUAL, 1, 5);

Time::HiRes::sleep(2);

getAccessToken();
getSomeIDs();

exit;


### subroutines

sub refreshAccessToken {
    my $timeout = getAccessToken();
    local $SIG{VTALRM} = \&refreshAccessToken;
    setitimer(ITIMER_VIRTUAL, $timeout, $timeout);
    die;
}

sub getAccessToken {
    say "getting access token";
    $accessToken = localtime(time());
    return 2;
}

sub getSomeIDs {

    my $loop = 1;

    #say "getting IDs: timeout == $timeout";

    while ($loop) {
        if ($counter >= 1600000000) { $loop = 0; }
        say "[ $accessToken ] $counter";
        $counter = $counter + 1;
    };
}

2 个答案:

答案 0 :(得分:2)

如果API给出了到期时间,这很容易解决。这是一种使用time和简单算术的方法。

sub get_access_token {
    # some token generation code
    return ( $token, $expire_time );
}

my ( $token, $expire_time ) = get_access_token;
my $expire_timestamp = time + $expire_time;

while (1) {
    if ( time > $expire_timestamp ) {
        ( $token, $expire_time ) = get_access_token;
        $expire_timestamp = time + $expire_time;
    }

    # make requests using $token
}

我对alarm不太熟悉,但我的尝试(根据文档)将如下所示,即设置一个SIGALRM处理程序来调用get_access_token()来续订token_expire_timing,然后再次将警报设置为token_expire_timing

my ( $token, $expire_time ) = get_access_token();

eval {
    local $SIG{ALRM} = sub {
        ( $token, $expire_time ) = get_access_token();
    };
    alarm $expire_time;    # schedule alarm in $expire_time
    while (1) {
        # your API requests here with $token
    }
    alarm 0; #cancel the alarm
};
if ($@) {
    die "$@\n"; #propagate some other errors
}

sub get_access_token {

    # some token generation code
    ( $token, $expire_time );
}

答案 1 :(得分:0)

好的,所以time()和算术一样;可爱走出窗外。感谢所有给出建议的人:

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

my $counter = 0;

my ($token, $expire_time, $expire_ts) = get_access_token();

my $loop = 1;
while ($loop) {
    if (time() > $expire_ts) {
        ($token, $expire_time, $expire_ts) = get_access_token();
    }
    if ($counter == 14) { $loop = 0; }
    say "[ $token ] $counter";
    $counter = $counter + 1;
    sleep(1);
}

exit;


### subroutines

sub get_access_token {
    my $token = localtime(time());
    my $expire_time = 3;
    my $expire_ts = time() + $expire_time;
    # some token generation code
    return ( $token, $expire_time, $expire_ts );
}

输出

$ ./timeTest.pl
[ Fri Feb 26 17:05:46 2016 ] 0
[ Fri Feb 26 17:05:46 2016 ] 1
[ Fri Feb 26 17:05:46 2016 ] 2
[ Fri Feb 26 17:05:46 2016 ] 3
[ Fri Feb 26 17:05:50 2016 ] 4
[ Fri Feb 26 17:05:50 2016 ] 5
[ Fri Feb 26 17:05:50 2016 ] 6
[ Fri Feb 26 17:05:50 2016 ] 7
[ Fri Feb 26 17:05:54 2016 ] 8
[ Fri Feb 26 17:05:54 2016 ] 9
[ Fri Feb 26 17:05:54 2016 ] 10
[ Fri Feb 26 17:05:54 2016 ] 11
[ Fri Feb 26 17:05:58 2016 ] 12
[ Fri Feb 26 17:05:58 2016 ] 13
[ Fri Feb 26 17:05:58 2016 ] 14