2016-04-17 33 views
7

Tôi đang phát xung quanh với Mojolicious và websockets. Tôi muốn gửi đầu ra của nhiều lệnh bên ngoài trên máy chủ đến trang web. Tôi không gặp vấn đề gì với việc kết nối và nhận tin nhắn, nhưng tôi cũng muốn gửi một tin nhắn trở lại máy chủ để dừng một lệnh bên ngoài trong khi cho phép người khác tiếp tục gửi tin nhắn lại cho khách hàng. Tôi cũng muốn dừng kiểm tra lệnh bên ngoài khi nó thoát.Tắt sự kiện định kỳ Mojo :: IOLoop được kết nối với Mojo websocket

Lệnh bên ngoài chỉ đơn giản là một lớp lót phun ra một số nguyên sau mỗi vài giây. Tôi có hai websockets hiển thị các số trong riêng biệt div s. Nhấp vào một trong các nút dừng gửi thông báo, nhưng đó là nơi tôi cần tìm ra cách tắt websocket đó (và chỉ websocket đó) và tắt lệnh bên ngoài.

enter image description here

Khi tôi kết nối các WebSocket, tôi chạy lệnh bên ngoài và thiết lập một Mojo::IOLoop->recurring để kiểm tra nếu có đầu ra.

Khi tôi muốn dừng lại, tôi cho rằng tôi nên gọi Mojo::IOLoop->remove($id), nhưng điều đó dường như không xóa hoàn toàn và tôi nhận được thông báo lỗi như Mojo::Reactor::Poll: Timer failed: Can't call method "is_websocket" on an undefined value.

Nếu tôi gọi finish trên đối tượng bộ điều khiển để tắt websocket, có vẻ như sẽ dừng mọi thứ.

Tôi có toàn bộ Mojolicious::Lite app as a gist, nhưng đây là những phần mà tôi

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

câu hỏi khác có thể được hưởng lợi từ câu trả lời này:

+0

Mojolicious có bật khai báo đối số phụ kiểu Perl6 không? – Zaid

+1

Perl v5.20 có chữ ký con là một tính năng thử nghiệm: http: //www.effectiveperlprogramming.com/2015/04/use-v5-20-subroutine-signatures/ –

Trả lời

1

Tôi đã chơi trò chơi này một chút. Logioniz's answer khiến tôi nghĩ rằng tôi không nên bỏ phiếu hoặc xử lý thông tin chi tiết về bản thân. Tôi vẫn không biết nó đang treo ở đâu.

Thay vào đó, tôi đã sử dụng Mojo::Reactor 's io để thiết lập một filehandle để giám sát:

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

Khi tôi đang thực hiện với lệnh đó, tôi có thể Ngừng theo dõi filehandle đó và giết chết quá trình.Tôi hoàn thành websocket:

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

Tôi vẫn không biết tại sao remove($fh) không hoạt động. Tôi hình tôi đang rò rỉ một số thứ IOLoop làm theo cách này.

0

Tôi nghĩ bạn chặn vòng lặp sự kiện bởi vì lệnh gọi lại lặp lại của bạn rất thứ hai và my $m = <$fh>; chờ kết quả khoảng 2-3 giây. Vì vậy, bạn chặn vòng lặp sự kiện. Tôi nghĩ vậy vì khi tôi chạy ứng dụng của bạn, sự kiện finish không gọi khi hết giờ không hoạt động, nhưng gọi sự kiện recurrent. finish sự kiện PHẢI gọi vào thời gian chờ không hoạt động luôn.

Tôi nghĩ rằng mã của bạn phải ở trong quá trình riêng biệt để tránh chặn vòng lặp sự kiện.

Hãy thử sử dụng this mô-đun để thực thi trong quá trình riêng biệt. I write ví dụ nhỏ.

Các vấn đề liên quan