2016-02-26 3 views
1

Хорошо, так что я в тупике. Вот что я, похоже, не могу решить: при вызове общедоступного API первое, что требуется, это токен доступа - позвоним мне мой метод get_access_token(). Как только я получаю токен доступа, я буду использовать его для получения данных из публичного API. Теперь токен истекает через определенное количество секунд, причем этот номер снабжен токеном доступа. Итак, идея состоит в том, чтобы настроить какой-то таймер, который обновит токен доступа, вызвав get_access_token() снова, как только предыдущий токен истечет, так что я могу продолжать получать данные из API, всегда с допустимым токеном доступа.Perl: многократно вызывает подпрограмму после (возможно меняющейся) задержки времени

Я попытался с помощью:

  • сигнализация
  • AnyEvent
  • Время :: HiRes-КЯ (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; 
    }; 
} 
+0

Вы только позвоните 'тревоги ($ таймаут),' один раз, так что тревога срабатывает только один раз. Вы должны вызывать его каждый раз, когда вы получаете новый токен доступа. – ThisSuitIsBlackNot

+0

Я думал, что он будет вызван несколько раз, потому что '$ SIG {ALRM}' установлен для вызова подпрограммы 'wrapper()', которая включает вызов 'getSomeIDs()', который вызывает 'alarm ($ timeout)'. – rwatkins

+0

Я просто попытался добавить 'alarm ($ timeout)' в 'getAccessToken()' подпрограмму - он не изменил поведение в малейшей степени. – rwatkins

ответ

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 ([email protected]) { 
    die "[email protected]\n"; #propagate some other errors 
} 

sub get_access_token { 

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

Да, конечно, но я надеялся избежать сотен, если не тысяч вызовов 'time()' для каждого токена доступа. – rwatkins

+0

Я также обновляю решение с помощью будильника, дайте мне немного времени. Выберите то, что подходит вам лучше всего. –

+0

Спасибо, с нетерпением жду! – rwatkins

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