2013-10-21 12 views
5

In un'applicazione Mojolicious, sto cercando di convertire i file ODT in HTML quando un link viene cliccato. Converto i file usando "soffice", un comando di shell. La conversione dei file richiede del tempo. Invio messaggi di stato all'utente per informarlo dell'avanzamento. Mando questi messaggi di aggiornamento di stato, scrivendo ad un Mojo :: Log oggetto. Quindi, sottoscrivo questo oggetto log in una rotta EventSource.Utilizzando AnyEvent run_cmd in Mojolicious, continuo a ricevere questo errore: "AnyEvent :: CondVar: ricorsiva blocco attesa tentato"

Poi ho un ciclo tra i file e utilizzare AnyEvent :: Util run_cmd per eseguire il programma esterno "soffice".

Abbastanza copiato e incollato dalle principali esercitazioni AnyEvent. Se ci sono solo pochi file da convertire (circa 2 o 3), allora tutto va bene. I messaggi di stato inviati tramite la connessione EventSource vengono visualizzati sul browser client. Quindi, dopo che tutti i file sono stati convertiti, viene visualizzata la pagina web.

Se più file vengono da elaborare, alcuni file vengono convertiti poi il messaggio di errore nel titolo del thread si verifica.

Il percorso per il percorso che contiene il codice di cui sopra è questo:

my $initdocs = $r->under->to('docroute#initdocs'); 
$initdocs->get('/showdocs')->to('docroute#showdocs'); 

Il codice di cui sopra è nel percorso "initdocs".

Qualsiasi aiuto è apprezzato. Grazie in anticipo.

+2

just fyi, in futuro, aggiungere un tag [tag: perl] aiuterà più persone a vedere le tue domande mojolicious. –

risposta

3

Creating single Thread Server with AnyEvent

AnyEvent recursive Blocking..

Se si utilizza AnyEvent, di solito si hanno a che fare con CondVars. Ci sono due cose che puoi fare con un CondVar: o si registra un callback che verrà chiamato quando viene attivato il CondVar, o si chiama recv e si bloccherà fino a quando il CondVar non verrà attivato. In un itinerario del vostro Mojo :: Controller, probabilmente avrete bisogno di bloccare fino a quando hai tutti i dati che si desidera visualizzare per l'utente.

adottare le seguenti (truccata) esempio che utilizza un CondVar:

non testati:

get '/' => sub { 
    ... 
    my $cv = AnyEvent->condvar; 
    my $timer = AnyEvent->timer(after => 1, cb => sub { $cv->send(1) }); 
    my $result = $cv->recv; 
    ... 
}; 

Si otterrà un errore di runtime che indica "AnyEvent :: CondVar: blocco ricorsivo aspettare rilevato". Forse perché Morbo utilizza anche un CondVar come exit_guard, per essere eseguito all'infinito (il blocco su un CondVar è un modo semplice per eseguire il ciclo principale).

Il mio approccio sarebbe quello di utilizzare un eventloop specifico, come EV, e chiamare EV-> anello invece di bloccare su un CondVar:

EV->loop 
4

Penso che quello che si sta cercando di fare è di chiamare il soffice (blocking) processo senza bloccare il resto del thread del server. Non sono un esperto di AE ma non penso che sia ciò che fa run_cmd. È più vicino a quello che lo fa fork_call. Forse ciò che si vuole fare è qualcosa di più simile a questo:

#!/usr/bin/env perl 

use Mojolicious::Lite; 
use EV; 
use AnyEvent::Util 'fork_call'; 

any '/' => sub { 
    my $c = shift; 
    $c->render_later; 
    fork_call { `sleep 5 && echo 'hi'` } sub { 
    my $data = shift; 
    $c->render(text => $data); 
    }; 
}; 

app->start; 

Nel mio esempio ho solo fare una semplice chiamata di blocco, ma si potrebbe facilmente chiamare a soffice.

Da quando si dice che potrebbe essere necessario convertire diversi file diversi prima di tornare al client, è possibile utilizzare l'eccellente Mojo::IOLoop::Delay per gestire i processi.

#!/usr/bin/env perl 

use Mojolicious::Lite; 
use EV; 
use AnyEvent::Util 'fork_call'; 

my @jobs = (
    q{sleep 5 && echo 'hi'}, 
    q{sleep 5 && echo 'bye'}, 
); 

any '/' => sub { 
    my $c = shift; 
    $c->render_later; 
    my $delay = Mojo::IOLoop->delay; 
    $delay->on(finish => sub { 
    shift; $c->render(text => join '', @_); 
    }); 
    fork_call { `$_` } $delay->begin(0) for @jobs; 
}; 

app->start; 

Ancora una volta, io sono solo catturare l'output e inviarlo alla chiamata di rendering, ma notare che attende tutti i lavori per terminare prima di tornare. Qualcosa di più vicino al tuo vero caso d'uso potrebbe essere:

#!/usr/bin/env perl 

use Mojolicious::Lite; 
use EV; 
use AnyEvent::Util 'fork_call'; 
use Capture::Tiny 'capture'; 

any '/' => sub { 
    my $c = shift; 
    my $files = $c->every_param('file'); 
    $c->render_later; 
    my $delay = Mojo::IOLoop->delay; 
    $delay->on(finish => sub { 
    shift; $c->render(json => \@_); 
    }); 
    my $output_dir = './output_dir'; 
    for my $file (@$files) { 
    my $cmd = "soffice --headless --convert-to html --outdir '$output_dir' '$file'"; 
    fork_call { [ capture { system $cmd } ] } $delay->begin(0); 
    } 
}; 

app->start; 

Questo viene eseguito soffice su ogni nome del file passato come parametro al percorso (/?file=myfile&file=otherfile). Quindi lo stdout, lo stderr e il codice di ritorno sono (beh dovrebbe essere, non ho eseguito questo ovviamente) resi come json al client (potreste facilmente loggarlo).

+0

Volevo solo menzionare (dal momento che questo post è saltato fuori) che questa domanda alla fine mi ha ispirato a rendere questo modulo su cpan: https://metacpan.org/pod/Mojo::IOLoop::ForkCall –

Problemi correlati