【问题标题】:Mojolicious REST + IOLoop concurrent serviceMojolicious REST + IOLoop 并发服务
【发布时间】:2013-07-01 11:47:35
【问题描述】:

首先我是 Perl 和 Mojo 的新手,所以我会尽我所能.. :D

问题在于我需要并行工作,因此该服务需要每秒处理多个调用。该服务将允许其他服务通过 REST 连接以下载和处理文件。我正在对这个想法进行一些测试,但没有成功。

我一直在围绕 Mojo::IOLoop 进行测试,但我认为我误解了一些概念,因为使用循环和计时器的每个实现都绝对错误...

实际上我的工作流程在这里:

(1) 获取 REST 调用

(2) 验证通过 mongodb 提供的 url

(3) 下载与该 mongo 对象关联的文件。

所以实际上这是可行的,但我看不出如何实现它以并行工作......

#!/usr/bin/env_perl
use Mojolicious::Lite;
use MongoDB;

my $mongo     = MongoDB::MongoClient->new(host => 'xxxx.mongohq.com',port => 'xxxxx',     username => 'xxxxx', password => 'xxxxx', db_name => 'xxxxx');
my $database   = $mongo->get_database( 'xxxxx' );
my $collection = $database->get_collection( 'xxxxx' );

any [qw/ get post /] => '/:action/:objid' => sub {

  my $self = shift;

  #Parsing objectid...
  my $objectid  = $self->param('objid');
  my $id = MongoDB::OID->new(value => $objectid);

  my $all = $collection->find({_id => $id});
  my $dts = $all->next;

  download($dts->{parsedoc}->{url},"final_name.xml");

};

app->start;

sub download {
  say "Starting new download..";  
  my $ua = Mojo::UserAgent->new(max_redirects => 5);
  my $tx = $ua->get($_[0]);
  $tx->res->content->asset->move_to($_[1]);
  say "Download ends."; 
}

在这里实现 IOLoop 的最佳方式是什么?我一直在使用一些 mojo 计时器示例,但也许我需要重新排序我的应用程序流程?提前致谢。

【问题讨论】:

  • IOLoop 不是并行工作,而是非阻塞工作。因此,您需要一个非阻塞的 Mongo 客户端。我知道 Mojolicious 的作者一直在写一个叫 Mango,但它目前处于 alpha 状态。

标签: perl rest parallel-processing mojolicious


【解决方案1】:

正如我在评论中提到的,Mojo::IOLoop 不关注并行性,而是关注事件循环。这意味着可能需要很长时间的操作可能会启动,然后仅在完成后继续,而不会阻塞其他进程。在您的示例中,对 Mongo 的调用和下载都是这些类型的长时间运行的进程,可能希望将其放入事件循环中。 Mojolicious::UserAgent 与回调一起使用时是非阻塞的(操作完成后调用的操作)。要对 Mongo 执行相同操作,您需要确保您的 Mongo 库可以以非阻塞方式使用。

我模拟了一个忽略 Mongo 部分(为读者练习)并以非阻塞方式使用 UserAgent 的小示例。

#!/usr/bin/env perl
use Mojolicious::Lite;

helper download => sub {
  my ($c, $url, $target) = @_;
  my $ua = $c->app->ua->max_redirects(5);

  say "Starting new download ($url) ..."; 
  $ua->get( $url, sub {
    my ($ua, $tx) = @_;
    say "Finished download ($url) ...";
    $tx->res->content->asset->move_to($target);

    say "File saved ($url --> $target)";
    $c->render( text => "File saved ($url --> $target)" );
  });
};

any [qw/ get post /] => '/:action/:objid' => sub {
  my $self = shift;
  $self->render_later; # prevent auto-render

  #Parsing objectid
  my $objectid  = $self->param('objid');
  #verify url here ...

  # example url
  my $url = 'http://www.google.com';

  $self->download($url,"final_name.xml");
};

app->start;

【讨论】:

  • 谢谢乔尔,我认为这就是重点。实际上,我迷失了 Perl 中的循环概念。尽管有一些不活动超时,我一直在测试您的解决方案并且工作正常(我认为这来自大型下载期间的“延迟”渲染)。
  • 该代码的第一次迭代刚刚呈现download started 并让下载继续在服务器端。那是一种可能性。另一种是向客户端打开一个 websocket 以使其了解进度。无论如何,这就是所有的实现。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-28
  • 2012-10-16
  • 1970-01-01
  • 1970-01-01
  • 2014-02-11
  • 1970-01-01
相关资源
最近更新 更多