在 Mojolicious 中同时获取数据
Fetching data simultaneously in Mojolicious
我正在尝试 运行 并行执行多个子例程(以从外部系统获取数据)。为了模拟,我在下面的示例中使用 sleep
。我的问题是:如何在 Mojolicious 中实现这一点?
#!/usr/bin/perl
use Mojolicious::Lite;
use Benchmark qw(:hireswallclock);
sub add1 { my $a = shift; sleep 1; return $a+1; }
sub mult2 { my $b = shift; sleep 1; return $b*2; }
sub power { my ($x, $y) = @_; sleep 1; return $x ** $y; }
any '/' => sub {
my ( $self ) = @_;
my $n = int(rand(5));
my $t0 = Benchmark->new;
my $x = mult2($n); # Need to run in parallel
my $y = add1($n); # Need to run in parallel
my $z = power($x,$y);
my $t1 = Benchmark->new;
my $t = timediff($t1,$t0)->real();
$self->render(text => "n=$n, x=$x, y=$y, z=$z;<br>T=$t seconds");
};
app->start;
换句话说,我想通过 运行ning (add1
& mult2
) 并行。
这个 thread 使用了一个 Mojo::IOLoop->timer
,这似乎与我的情况无关?如果是这样,我不知道如何使用它。谢谢!
为避免长时间等待,您可以使用 Mojolicious non-blocking 操作。不要 运行 向外部系统发出同步请求,而是使用 non-blocking 方法来代替 运行 一些完成后的回调。例如。为避免 sleep
,我们将使用 Mojo::IOLoop->timer(...)
.
这是您的代码的变体,它使用 non-blocking 操作,并使用 Promises 正确排序函数:
use Mojolicious::Lite;
use Benchmark qw(:hireswallclock);
# example using non-blocking APIs
sub add1 {
my ($a) = @_;
my $promise = Mojo::Promise->new;
Mojo::IOLoop->timer(1 => sub { $promise->resolve($a + 1) });
return $promise;
}
# example using blocking APIs in a subprocess
sub mult2 {
my ($b) = @_;
my $promise = Mojo::Promise->new;
Mojo::IOLoop->subprocess(
sub { # first callback is executed in subprocess
sleep 1;
return $b * 2;
},
sub { # second callback resolves promise with subprocess result
my ($self, $err, @result) = @_;
return $promise->reject($err) if $err;
$promise->resolve(@result);
},
);
return $promise;
}
sub power {
my ($x, $y) = @_;
my $result = Mojo::Promise->new;
Mojo::IOLoop->timer(1 => sub { $result->resolve($x ** $y) });
return $result;
}
any '/' => sub {
my ( $self ) = @_;
my $n = int(rand(5));
my $t0 = Benchmark->new;
my $x_promise = mult2($n);
my $y_promise = add1($n);
my $z_promise = Mojo::Promise->all($x_promise, $y_promise)
->then(sub {
my ($x, $y) = map { $_->[0] } @_;
return power($x, $y);
});
Mojo::Promise->all($x_promise, $y_promise, $z_promise)
->then(sub {
my ($x, $y, $z) = map { $_->[0] } @_;
my $t1 = Benchmark->new;
my $t = timediff($t1, $t0)->real();
$self->render(text => "n=$n, x=$x, y=$y, z=$z;\nT=$t seconds\n");
})
->wait;
};
app->start;
这比您的代码复杂得多,但在两秒而不是三秒内完成,并且不会阻止同时发生的其他请求。所以你可以一次请求这条路线 30 次,两秒后得到 30 个响应!
请注意,Promise->all
returns 一个具有所有等待承诺值的承诺,但将每个承诺的值放入一个数组引用中,因此我们需要解压缩它们以获取实际值。
如果您不能使用 non-blocking 操作,您可以 运行 子进程中的阻塞代码,使用 Mojo::IOLoop->subprocess(...)
。您仍然可以通过承诺协调数据流。例如。有关示例,请参见上面的 mult2
函数。
并发与并行
为了同时(并行)发生两个事件,您需要多个处理单元。
例如,对于单个 CPU,您在任何时候只能执行一个数学运算,因此无论 并发 代码中的主题。
Non-mathematical 操作,例如 input/output(例如网络、硬盘驱动器)可以以 并行 方式发生,因为这些在大多数情况下独立于您的单个 CPU(我将不解释 multi-core 系统,因为一般来说 Perl 并未针对它们的使用进行优化)。
Mojolicious 的产品 web-server Hypnotoad 依赖于 non-blocking IO 的正确实施。因此,他们提供了 non-blocking user-agent 作为控制器的一部分。
$controller->ua->get(
$the_url,
sub {
my ( $ua, $tx ) = @_;
if ( my $result = $tx->success ) {
# do stuff with the result
}
else {
# throw error
}
}
);
您可以在此处实施 Mojo::Promise 以改进您的代码流程。
如果可能,我建议在从 "external systems" 获取数据时实施 non-blocking UA。如果您发现您的 Hypnotoad 工作进程阻塞时间过长(5 秒),它们很可能会被杀死并被替换,这可能会破坏您的系统。
我正在尝试 运行 并行执行多个子例程(以从外部系统获取数据)。为了模拟,我在下面的示例中使用 sleep
。我的问题是:如何在 Mojolicious 中实现这一点?
#!/usr/bin/perl
use Mojolicious::Lite;
use Benchmark qw(:hireswallclock);
sub add1 { my $a = shift; sleep 1; return $a+1; }
sub mult2 { my $b = shift; sleep 1; return $b*2; }
sub power { my ($x, $y) = @_; sleep 1; return $x ** $y; }
any '/' => sub {
my ( $self ) = @_;
my $n = int(rand(5));
my $t0 = Benchmark->new;
my $x = mult2($n); # Need to run in parallel
my $y = add1($n); # Need to run in parallel
my $z = power($x,$y);
my $t1 = Benchmark->new;
my $t = timediff($t1,$t0)->real();
$self->render(text => "n=$n, x=$x, y=$y, z=$z;<br>T=$t seconds");
};
app->start;
换句话说,我想通过 运行ning (add1
& mult2
) 并行。
这个 thread 使用了一个 Mojo::IOLoop->timer
,这似乎与我的情况无关?如果是这样,我不知道如何使用它。谢谢!
为避免长时间等待,您可以使用 Mojolicious non-blocking 操作。不要 运行 向外部系统发出同步请求,而是使用 non-blocking 方法来代替 运行 一些完成后的回调。例如。为避免 sleep
,我们将使用 Mojo::IOLoop->timer(...)
.
这是您的代码的变体,它使用 non-blocking 操作,并使用 Promises 正确排序函数:
use Mojolicious::Lite;
use Benchmark qw(:hireswallclock);
# example using non-blocking APIs
sub add1 {
my ($a) = @_;
my $promise = Mojo::Promise->new;
Mojo::IOLoop->timer(1 => sub { $promise->resolve($a + 1) });
return $promise;
}
# example using blocking APIs in a subprocess
sub mult2 {
my ($b) = @_;
my $promise = Mojo::Promise->new;
Mojo::IOLoop->subprocess(
sub { # first callback is executed in subprocess
sleep 1;
return $b * 2;
},
sub { # second callback resolves promise with subprocess result
my ($self, $err, @result) = @_;
return $promise->reject($err) if $err;
$promise->resolve(@result);
},
);
return $promise;
}
sub power {
my ($x, $y) = @_;
my $result = Mojo::Promise->new;
Mojo::IOLoop->timer(1 => sub { $result->resolve($x ** $y) });
return $result;
}
any '/' => sub {
my ( $self ) = @_;
my $n = int(rand(5));
my $t0 = Benchmark->new;
my $x_promise = mult2($n);
my $y_promise = add1($n);
my $z_promise = Mojo::Promise->all($x_promise, $y_promise)
->then(sub {
my ($x, $y) = map { $_->[0] } @_;
return power($x, $y);
});
Mojo::Promise->all($x_promise, $y_promise, $z_promise)
->then(sub {
my ($x, $y, $z) = map { $_->[0] } @_;
my $t1 = Benchmark->new;
my $t = timediff($t1, $t0)->real();
$self->render(text => "n=$n, x=$x, y=$y, z=$z;\nT=$t seconds\n");
})
->wait;
};
app->start;
这比您的代码复杂得多,但在两秒而不是三秒内完成,并且不会阻止同时发生的其他请求。所以你可以一次请求这条路线 30 次,两秒后得到 30 个响应!
请注意,Promise->all
returns 一个具有所有等待承诺值的承诺,但将每个承诺的值放入一个数组引用中,因此我们需要解压缩它们以获取实际值。
如果您不能使用 non-blocking 操作,您可以 运行 子进程中的阻塞代码,使用 Mojo::IOLoop->subprocess(...)
。您仍然可以通过承诺协调数据流。例如。有关示例,请参见上面的 mult2
函数。
并发与并行
为了同时(并行)发生两个事件,您需要多个处理单元。
例如,对于单个 CPU,您在任何时候只能执行一个数学运算,因此无论 并发 代码中的主题。
Non-mathematical 操作,例如 input/output(例如网络、硬盘驱动器)可以以 并行 方式发生,因为这些在大多数情况下独立于您的单个 CPU(我将不解释 multi-core 系统,因为一般来说 Perl 并未针对它们的使用进行优化)。
Mojolicious 的产品 web-server Hypnotoad 依赖于 non-blocking IO 的正确实施。因此,他们提供了 non-blocking user-agent 作为控制器的一部分。
$controller->ua->get(
$the_url,
sub {
my ( $ua, $tx ) = @_;
if ( my $result = $tx->success ) {
# do stuff with the result
}
else {
# throw error
}
}
);
您可以在此处实施 Mojo::Promise 以改进您的代码流程。
如果可能,我建议在从 "external systems" 获取数据时实施 non-blocking UA。如果您发现您的 Hypnotoad 工作进程阻塞时间过长(5 秒),它们很可能会被杀死并被替换,这可能会破坏您的系统。