2016-04-17 17 views
7

Sto giocando con Mojolicious e websockets. Voglio inviare l'output di più comandi esterni sul server alla pagina web. Non ho problemi con la connessione e la ricezione di messaggi, ma desidero anche inviare un messaggio al server per interrompere un comando esterno lasciando che gli altri continuino a inviare messaggi al client. Voglio anche smettere di controllare il comando esterno quando esce.Arresto di un evento Mojo :: IOLoop collegato a una rete Web Mojo

Il comando esterno è semplicemente un one-liner che emette un numero intero ogni pochi secondi. Ho due web socket che visualizzano i numeri in separati div s. Facendo clic su uno dei pulsanti di arresto viene inviato il messaggio, ma è lì che devo capire come spegnere quel websocket (e solo quel websocket) e chiudere il comando esterno.

enter image description here

quando collego il websocket, corro il comando esterno e creare un Mojo::IOLoop->recurring per verificare se c'è uscita.

Quando voglio fermarmi, immagino che dovrei chiamare Mojo::IOLoop->remove($id), ma questo non sembra rimuoverlo completamente e ricevo messaggi di errore come Mojo::Reactor::Poll: Timer failed: Can't call method "is_websocket" on an undefined value.

Se chiamo finish sull'oggetto controller per arrestare il websocket, sembra che tutto si fermi.

devo tutto Mojolicious::Lite app as a gist, ma qui è la parte dove mi

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

Altre questioni che possono beneficiare di questa risposta:

+0

Mojolicious abilita la dichiarazione di sottotitoli in stile Perl6? – Zaid

+1

Perl v5.20 ha firme di subroutine come funzionalità sperimentale: http: //www.effectiveperlprogramming.com/2015/04/use-v5-20-subroutine-signatures/ –

risposta

1

Ho giocato un po 'con questo. Logioniz's answer mi ha fatto pensare che non dovrei interrogare o gestire i dettagli del filehandle da solo. Non so ancora dove fosse appeso.

Invece, ho usato Mojo::Reactor s' io per impostare un filehandle per il monitoraggio:

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

Quando ho finito con questo comando, posso unwatch che filehandle e uccidere il processo.Finisco il websocket:

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

io ancora non so perché remove($fh) non funziona. Immagino che sto perdendo alcune cose di IOLoop che lo fanno in questo modo.

0

credo che blocchi il ciclo degli eventi perché il tuo ricorrente richiama e molto secondo e my $m = <$fh>; attendere il risultato di circa 2-3 secondi. Quindi blocchi il ciclo degli eventi. Penso di sì perché quando eseguo la tua app l'evento finish non richiama il timeout di inattività, ma chiama l'evento recurrent. L'evento finish DEVE chiamare sempre il timeout di inattività.

Penso che il codice debba essere in un processo separato per evitare il blocco del ciclo degli eventi.

Prova ad utilizzare il modulo this da eseguire in un processo separato. I write piccolo esempio.