Завершение повторяющегося события Mojo :: IOLoop, подключенного к веб-сокету Mojo

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

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

введите описание изображения здесь

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

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

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

У меня есть все приложение Mojolicious :: Lite как суть, но здесь я

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;
    }

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


person brian d foy    schedule 17.04.2016    source источник
comment
Разрешает ли Mojolicious объявление подаргументов в стиле Perl6?   -  person Zaid    schedule 17.04.2016
comment
Perl v5.20 имеет подписи подпрограмм в качестве экспериментальной функции: эффективное перлпрограммирование .com / 2015/04 /   -  person brian d foy    schedule 17.04.2016


Ответы (2)


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

Вместо этого я использовал io Mojo :: Reactor, чтобы установить дескриптор файла для отслеживания:

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 );
    }

Когда я закончу с этой командой, я могу отменить отслеживание этого дескриптора файла и убить процесс. Дорабатываю веб-сокет:

    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, делая это таким образом.

person brian d foy    schedule 22.04.2016

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

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

Попробуйте использовать этот модуль для выполнения в отдельном процессе. Я напишу небольшой пример.

person Logioniz    schedule 18.04.2016