2013-10-21 34 views
5

W aplikacji Mojolicious, próbuję przekonwertować pliki ODT do formatu HTML, gdy kliknięciu łącza. Konwertuję pliki za pomocą "soffice", polecenia powłoki. Konwersja plików zajmuje trochę czasu. Wysyłam komunikaty o stanie do użytkownika, aby powiadomić go o postępie. Wysyłam te komunikaty o aktualizacji statusu, pisząc do obiektu Mojo :: Log. Następnie subskrybuję ten obiekt dziennika na trasie EventSource.Korzystanie AnyEvent run_cmd w Mojolicious, wciąż otrzymuję ten błąd: „AnyEvent :: CondVar: rekurencyjny blokowanie czekać próbę”

Potem pętla poprzez pliki i korzystać AnyEvent :: Util run_cmd wykonać zewnętrznego programu „soffice”.

for my $file (@{ $filelist }) { 
    my $output_dir = './output_dir'; 
    my $cmd = "soffice --headless --convert-to html --outdir '$output_dir' '$file'"; 
    my $cv = AnyEvent->condvar; 
    my $w; 
    $w = run_cmd($cmd, 
       '>' => sub { my $out = shift; 
           &WriteToLog({ status => "cmd output '$out'..." }); 
           undef $w; 
           $cv->send; 
       }, 

       '2>' => sub { my $err = shift; 
           &WriteToLog({ status => "ERROR '$err'..." }); 
           undef $w; 
           $cv->send; 
       } 
      ); 

    $cv->recv; 
} 

Prawie skopiowane i wklejone z głównych samouczków AnyEvent. Jeśli istnieje tylko kilka plików do konwersji (około 2 lub 3), wtedy wszystko pójdzie dobrze. Komunikaty o stanie wysyłane za pośrednictwem połączenia EventSource są wyświetlane w przeglądarce klienta. Następnie po wszystkie pliki zostały przeliczone, strona internetowa jest renderowane.

Jeśli ma zostać przetworzonych więcej plików, kilka plików zostanie przekonwertowanych, pojawi się komunikat o błędzie w tytule wątku.

Routing dla trasy zawierającą kod powyżej to:

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

Kod powyżej w „initdocs” trasy.

Każda pomoc jest doceniana. Z góry dziękuję.

+2

Po prostu fyi, w przyszłości dodanie tagu [tag: perl] pomoże większej liczbie osób zobaczyć twoje mojolicious pytania. –

Odpowiedz

3

Creating single Thread Server with AnyEvent

AnyEvent recursive Blocking..

Jeśli używasz AnyEvent, zwykle mamy do czynienia z CondVars. Są dwie rzeczy, które można zrobić z CondVar: Albo się zarejestrować wywołania zwrotnego, która będzie wywoływana, gdy CondVar jest wyzwalany lub zadzwonić recv i będzie blokować aż zostanie wyzwolony CondVar. Na trasie swojej Mojo :: Controller, prawdopodobnie chcesz, aby zablokować dopóki masz wszystkie dane, które chcesz wyświetlić do użytkownika.

Weźmy następujący (gotowych) Przykład który wykorzystuje CondVar:

niesprawdzone:

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

Dostaniesz błąd wykonania stwierdzając "AnyEvent :: CondVar: rekurencyjny blokowanie czekać detected". Być może dzieje się tak dlatego, że Morbo używa także CondVar jako exit_guard, aby działać nieskończenie długo (blokowanie CondVar jest łatwym sposobem na uruchomienie głównej pętli).

Moje podejście byłoby użyć konkretnego eventloop, takiego jak EV i wywołać EV-> pętlę zamiast blokowania na CondVar:

EV->loop 
4

myślę, co chce zrobić, to zadzwonić do soffice (blokowanie) procesu bez blokowania reszty wątku serwera. Nie jestem ekspertem od AE, ale nie wydaje mi się, żeby to robił run_cmd. Jest jednak bliżej tego, co robi fork_call. Być może to, co chcesz zrobić, to coś więcej tak:

#!/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; 

W moim przykładzie po prostu zrobić prosty blokowania połączeń, ale można równie łatwo zawołać soffice.

Teraz, odkąd mówisz, że musisz przekonwertować kilka różnych plików przed powrotem do klienta, możesz użyć doskonałego Mojo::IOLoop::Delay do zarządzania procesami.

#!/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; 

Po raz kolejny, jestem po prostu uchwycenie wyjście i wysłanie go do wywołania renderowania, ale zauważ, że czeka na wszystkie zadania, aby zakończyć przed powrotem. Coś bliżej rzeczywistym przypadków użycia może być:

#!/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; 

ten biegnie soffice na każdej nazwy pliku przekazanego jako param do trasy (/?file=myfile&file=otherfile). Następnie standardowe wyjście, stderr i kod powrotu są (no, powinno być, ja tego oczywiście nie uruchomiłem) renderowane jako json do klienta (równie łatwo mógłbyś go zarejestrować).

+0

Chciałem tylko wspomnieć (od tego postu), że to pytanie ostatecznie zainspirowało mnie do stworzenia tego modułu na cpan: https://metacpan.org/pod/Mojo::IOLoop::ForkCall –