2016-04-17 2 views
7

Я играю с Mojolicious и websockets. Я хочу отправить вывод нескольких внешних команд на сервер на веб-страницу. У меня нет проблем с подключением и получением сообщений, но я также хочу отправить сообщение на сервер, чтобы остановить внешнюю команду, позволяя другим отправлять сообщения обратно клиенту. Я также хочу остановить проверку внешней команды после ее выхода.Выключение Mojo :: Повторное событие IOLoop, связанное с websocket Mojo

Внешняя команда - это просто один лайнер, который выплескивает целое число каждые несколько секунд. У меня есть два websockets, которые отображают числа в отдельных div s. Нажатие одной из кнопок останова отправляет сообщение, но мне нужно выяснить, как закрыть этот веб-узел (и только этот веб-узел) и отключить внешнюю команду.

enter image description here

При подключении WebSocket, я запускаю внешнюю команду и настроить Mojo::IOLoop->recurring, чтобы проверить, есть ли выход.

Когда я хочу остановиться, я полагаю, что я должен позвонить Mojo::IOLoop->remove($id), но это, похоже, не полностью удаляет его, и я получаю сообщения об ошибках, такие как Mojo::Reactor::Poll: Timer failed: Can't call method "is_websocket" on an undefined value.

Если я вызываю finish на объект контроллера, чтобы закрыть websocket, он, кажется, остановит все.

У меня есть весь Mojolicious::Lite app as a gist, но вот детали, где я

use feature qw(signatures); 
no warnings qw(experimental::signatures); 
## other boilerplate redacted 

websocket '/find' => sub ($c) { 
    state $loop = Mojo::IOLoop->singleton; 

    app->log->debug("websocket for find"); 
    $c->inactivity_timeout(50); 

    my $id; 
    $c->on(message => sub ($ws, $message) { 
     my $json = decode_json($message); 
     my $command = $json->{c}; 
     my $name = $json->{n}; 

     app->log->debug("Got $command command for $name"); 
     if($command eq "start") { 
      $id = run_command($ws); 
      app->log->debug("run_command for $name returned [$id]"); 
      } 
     elsif($command eq "stop") { 
      app->log->debug("stopping loop for $name [$id]"); 
      # XXX What should I do here? 
      # $ws->finish; 
      # $loop->remove($id); 
      } 
     elsif($command eq "open") { 
      app->log->debug("opening websocket for $name"); 
      } 
     } 
     ); 
    $c->on(
     finish => sub ($c, $code) { 
      app->log->debug("WebSocket closed with status $code"); 
      } 
     ); 
    }; 

app->start; 

sub run_command ($ws) { 
    app->log->debug("In run_command: $ws"); 
    open my $fh, "$^X -le '\$|++; while(1) { print int rand(100); sleep 3 }' |"; 
    $fh->autoflush; 

    my $id; 
    $id = Mojo::IOLoop->recurring(1 => sub ($loop) { 
     my $m = <$fh>; 
     unless(defined $m) { 
      app->log->debug("Closing down recurring loop from the inside [$id]"); 
      # XXX: what should I do here? 
      close $fh; 
      return; 
      }; 
     chomp $m; 
     app->log->debug("Input [$m] for [$id] from $fh"); 
     $ws->send(encode_json({ 'm' => $m })); 
     }); 

    return $id; 
    } 

Другие вопросы, которые могут извлечь выгоду из этого ответа:

+0

Включает ли Mojolicious объявление подзаголовка в стиле Perl6? – Zaid

+1

Perl v5.20 имеет подпрограммы в качестве экспериментальной функции: http: //www.effectiveperlprogramming.com/2015/04/use-v5-20-subroutine-signatures/ –

ответ

1

Я немного поиграл с этим. Logioniz's answer заставлял меня думать, что я не должен сам опросить или обработать данные дескриптора файла. Я до сих пор не знаю, где она висит.

Вместо этого я использовал Mojo::Reactor «ы io установить дескриптор для мониторинга:

sub run_command ($ws) { 
    my $pid = open my $fh, "$^X -le '\$|++; print \$\$; while(1) { print int rand(100); sleep 3 }' |"; 
    $fh->autoflush; 

    my $reactor = Mojo::IOLoop->singleton->reactor->io(
     $fh => sub ($reactor, $writeable) { 
      my $m = <$fh>; 
      chomp $m; 
      $ws->send(encode_json({ 'm' => $m })); 
      } 
     ); 

    return ($fh, $pid); 
    } 

Когда я закончу с этой командой, я могу unwatch, что и убить дескриптор процесса.Я заканчиваю WebSocket:

elsif($command eq "stop") { 
     $loop->reactor->watch($fh, 0, 0); 
     kill 'KILL', $pid or app->log->debug("Could not kill $pid: $!"); 
     $ws->finish; 
     } 

Я до сих пор не знаю, почему remove($fh) не работает. Я полагаю, что я пропущу некоторые вещи IOLoop, которые делают это так.

0

Я думаю, что вы блокируете цикл событий, потому что ваш повторяющийся вызов e очень второй и my $m = <$fh>; ждать результата около 2-3 секунд. Таким образом, вы блокируете цикл событий. Я так думаю, потому что, когда я запускаю ваше приложение, событие finish не вызывается неактивным таймаутом, но вызывает событие recurrent. finish событие ДОЛЖНО называть тайм-аут бездействия.

Я думаю, что ваш код должен быть в отдельном процессе, чтобы избежать блокировки цикла событий.

Попробуйте использовать модуль this для выполнения в отдельном процессе. I write маленький пример.

Смежные вопросы