【问题标题】:How to manage a long running process in a Catalyst App?如何在 Catalyst 应用程序中管理长时间运行的进程?
【发布时间】:2017-06-04 05:17:33
【问题描述】:

这是我的第一个 Catalyst 应用程序,我不确定如何解决以下问题。

用户在表单中输入一些数据并选择一个文件(最大 100MB)进行上传。提交表单后,实际计算最多需要 5 分钟,并将结果存储在 DB 中。

我想要做的是在后台中运行这个过程(也许还有文件上传)以避免服务器超时。应该向用户提供某种反馈(例如消息“工作已开始”或进度条)。作业仍在运行时应阻止表单。作业完成后应显示结果页面。

在阅读数小时后,我偶然发现了异步请求、作业队列、守护进程、GearmanCatalyst::Plugin::RunAfterRequest 等概念。

你会怎么做? 感谢您帮助网络开发新手!

PS:在我当前的本地应用程序中,工作是与 Parallel::ForkManager 并行完成的。对于真正的应用程序,是否建议使用像 Amazon EC2 这样的云计算服务?或者只是找一个提供多核服务器的主机?

【问题讨论】:

  • 将上传作为异步请求进行是有意义的。返回一个作业 ID 并在完成后让操作在模型中设置一个标志。然后让您的页面定期异步轮询后端(例如每 10 秒),如果它得到 done,则刷新页面。一会儿我会输入答案。
  • 关于您的托管问题,这实际上取决于用例。亚马逊或其他云服务的优点是在必要时易于扩展,但除了其他考虑因素外,可能比拥有自己的服务器更昂贵。这个问题最好单独和其他地方发布。
  • @simbabque 如果你有时间,有用的工具/插件或一些示例代码的提示会非常有帮助。感谢 bytepusher,我会在应用投入生产时考虑
  • 下面 Julien 的回答对纯后端很有帮助。但我们需要知道更多。您正在谈论并行处理之类的东西。你的申请有学术研究背景吗?如果因为用户不多而需要一段时间可以吗?还是这是商业广告,应该尽可能快?
  • 这是一个用于研究目的的生物信息学应用程序。也许以后也用于商业用途。基本上它在一个大文件中搜索文本字符串,提取序列,将结果存储在数据库中,并将它们可视化。在初始阶段,该应用程序将主要由 1 或 2 人同时使用。无论如何,在我的本地开发环境中,我使用 ForkManager 的 32 个线程,因此一次运行大约需要。 1分钟没问题。如果任务没有并行化,计算量会增加七倍。

标签: perl asynchronous cloud daemon catalyst


【解决方案1】:

不知何故,我无法理解 File::Queue。对于非阻塞并行执行,我最终使用了 TheSchwartz 和 Parallel::Prefork 的组合,就像在 Foorum Catalyst App 中实现的一样。 基本上,有5个重要元素。也许这个总结对其他人有帮助。

1) TheSchwartz DB

2) TheSchwartz DB 的客户端(DB 句柄)

package MyApp::TheSchwartz::Client;

use TheSchwartz;    
sub theschwartz {
    my $theschwartz = TheSchwartz->new(
        databases => [ {
            dsn  => 'dbi:mysql:theschwartz',
            user => 'user',
            pass => 'pass',
        } ],
        verbose => 1,
    );
    return $theschwartz;
}

3) 工作人员(实际工作完成的地方)

package MyApp::TheSchwartz::Worker::Test;

use base qw( TheSchwartz::Moosified::Worker );  
use MyApp::Model::DB;      # Catalyst DB connect_info
use MyApp::Schema;         # Catalyst DB schema   

sub work {
    my $class = shift;
    my $job = shift;    
    my ($args) = $job->arg;
    my ($arg1, $arg2) = @$args;

    # re-use Catalyst DB schema    
    my $connect_info = MyApp::Model::DB->config->{connect_info};
    my $schema = MyApp::Schema->connect($connect_info);

    # do the heavy lifting

    $job->completed();
}

4) 一个工作进程TheSchwartzWorker.pl 不间断地监控表job

use MyApp::TheSchwartz::Client qw/theschwartz/;    # db connection
use MyApp::TheSchwartz::Worker::Test;
use Parallel::Prefork;

my $client = theschwartz();

my $pm = Parallel::Prefork->new({
    max_workers  => 16,
    trap_signals => {
        TERM => 'TERM',
        HUP  => 'TERM',
        USR1 => undef,
    }
});

while ($pm->signal_received ne 'TERM') {
    $pm->start and next;

    $client->can_do('MyApp::TheSchwartz::Worker::Test');    
    my $delay = 10;    # When no job is available, the working process will sleep for $delay seconds
    $client->work( $delay );

    $pm->finish;
}    
$pm->wait_all_children();

5) 在 Catalyst 控制器中:将新作业插入表 job 并传递一些参数

use MyApp::TheSchwartz::Client qw/theschwartz/;
sub start : Chained('base') PathPart('start') Args(0) {
    my ($self, $c ) = @_;

    $client = theschwartz();
    $client->insert(‘MyApp::TheSchwartz::Worker::Test’, [ $arg1, $arg2 ]);

    $c->response->redirect(
        $c->uri_for(
            $self->action_for('archive'),
            {mid => $c->set_status_msg("Run '$name' started")}
        )
    );
}

新的运行在“存档”页面上显示为灰色,直到数据库中的所有结果都可用。

【讨论】:

    【解决方案2】:

    将作业放入队列中,并在 Web 应用程序之外的不同进程中执行。当您的 Catalyst 进程很忙时,即使使用 Catalyst::Plugin::RunAfterRequest,它也不能用于处理其他 Web 请求。

    有非常简单的排队系统,例如File::Queue。基本上,您为文档分配一个作业 ID,将其放入队列中。另一个进程检查队列并选择新作业。

    您可以将作业状态保存在数据库中,或任何可访问的任何 Web 应用程序中。在前端,您可以每 X 秒或每分钟轮询一次作业状态,以向用户提供反馈。

    您必须弄清楚您需要多少内存和 CPU。即使您有多个进程正在运行,也可能不需要多核 CPU 或多个 CPU。在专用服务器或像 EC2 这样的云之间进行选择更多的是关于灵活性(调整大小、快照等)与价格。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-30
    • 2010-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-03
    • 1970-01-01
    相关资源
    最近更新 更多