免责声明:我认为这个问题归结为如何让程序的两个不同组件相互交互以创建一个可从 Web 访问的应用程序。如果这不是您想要的,请将其视为深思熟虑。
通用网关接口
您在问题中谈论的是 CGI 脚本。 (强调我的)。
我正在尝试开发一个基于生产者-消费者范式的 Perl 应用程序。其中一个脚本创建一个包含数据的文件,而另一个则读取数据并以 HTML 的形式呈现。
一般来说,CGI 的工作方式是请求通过 Web 服务器,然后传递给应用程序。该应用程序可能是用 Perl 编写的。如果它是 Perl 脚本,则该脚本由 perl 解释器运行。 Web 服务器启动该过程。它可以通过CGI访问请求信息,主要是环境变量。该过程完成后,它将数据写入 STDOUT,Web 服务器将其作为响应并发回。
+-----------+ +-------------+ +----------------+
| | +----> | | +-----Request-----> | |
| Browser | | Web server | | perl foo.cgi |
| | <----+ | | <-----Response----+ | |
+-----------+ +-------------+ +----------------+
现在因为 Web 服务器后面只涉及一个进程,所以您不能有两个脚本。服务器无法同时与两个事物进行通信。这不是 CGI 的工作原理。
综合方法
相反,您需要将两个脚本包装到一个入口点,并将它们转换为某种组件。然后您可以让它们在内部相互通信,而在外部,Web 服务器只等待一个程序完成。
+-----------+ +-------------+ +-----------------+
| | +----> | | +-----Request-----> | |
| Browser | | Web server | | perl foo.cgi |
| | <----+ | | <-----Response----+ | |
+-----------+ +-------------+ | +-------------+ |
| | Producer | |
| +-----+-------+ |
| | |
| | |
| V |
| +-------------+ |
| | Consumer | |
| +-------------+ |
| |
+-----------------+
要将其翻译成 Perl,我们首先要确定一些术语。
-
脚本:一个位于 .pl 文件中且没有自己的
package 的 Perl 程序
-
module:一个 Perl 模块,它位于 .pm 文件中,并且有一个
package 和一个适合文件名的命名空间
假设您有这两个 Perl 脚本,我们称之为 producer.pl 和 consumer.pl。它们被大大简化,不考虑任何参数。
producer.pl
#!/usr/bin/perl
use strict;
use warnings 'all';
use CGI;
open my $fh, '>', 'product.data' or die $!;
print $fh "lots of data\n";
close $fh;
consumer.pl
#!/usr/bin/perl
use strict;
use warnings 'all';
use CGI;
my $q = CGI->new;
print $q->header('text/plain');
open my $fh, '<', 'product.data' or die $!;
while my $line (<$fh>) {
print $line;
}
exit;
这是尽可能简化的。有一个创建数据的脚本和一个使用它的脚本。现在我们需要让这两个交互而不实际运行它们。
让我们继续假设我们已经重构了这两个脚本并将它们转换为模块。我们稍后会看到它是如何工作的。我们现在可以在新的 foo.pl 脚本中使用这些模块。它将处理请求,向生产者请求数据,并让消费者将数据转换为读者想要的格式。
foo.pl
#!/usr/bin/perl
use strict;
use warnings 'all';
use Producer; # this was producer.pl
use Consumer; # this was consumer.pl
use CGI;
my $q = CGI->new;
my $params; # those would come from $q and are the parameters for the producer
my $product = Producer::produce($params);
my $output = Consumer::consume($product);
print $q->header;
print $output;
exit;
这很简单。我们从 CGI 读取参数,将它们传递给生产者,然后将产品传递给消费者。这给了我们输出,我们将其打印出来,然后返回到服务器,服务器发送响应。
让我们看看我们如何将这两个脚本变成简单的模块。那些不需要是面向对象的,尽管这可能是首选。请注意,文件名的拼写现在不同了。模块名称通常以大写字母开头。
Producer.pm
package Producer;
use strict;
use warnings 'all';
sub produce {
my @args = @_;
return "lots of data\n";
}
1;
Consumer.pm
package Consumer;
use strict;
use warnings 'all';
sub consume {
my ($data) = @_;
return $data; # this is really simple
}
1;
现在我们有两个模块,如果您调用正确的函数,它们的作用与脚本相同。我所做的只是在顶部放置一个命名空间 (package) 并将代码包装在 sub 中。我还删除了 CGI 部分。
在我们的示例中,生产者不必写入文件。它可以只返回数据结构。消费者反过来不需要从文件中读取。它只需要一个带有数据结构的变量并对其进行处理以呈现它。
如果您坚持使用一致的函数名称(例如 produce 和 consume,会更好),您甚至可以编写多个生产者或消费者。我们这里基本上定义了一个接口。这使我们有可能在不破坏兼容性的情况下重构代码的内部结构,而且还可以坚持完全不同的生产者或消费者。您可以从单行字符串生成器切换到在心跳中查找数据库中的内容的生成器,只要您坚持您的界面。
本质上,我们刚才所做的也可以这样显示:
+--foo.pl---------------------------+
| |
| +------+ +-------------+ |
| | | +----> | | |
| | | | Producer | |
| | | <----+ | | |
| | main | +-------------+ |
| | foo | |
| | body | +-------------+ |
| | | +----> | | |
| | | | Consumer | |
| | | <----+ | | |
| +------+ +-------------+ |
| |
+-----------------------------------+
这可能看起来有点眼熟。它本质上是Model-View-Controller (MVC) 模式。在 Web 上下文中,模型和视图通常只通过控制器相互通信,但几乎相同。
我们的生产者是一个数据模型。消费者将数据变成用户可以看到的网站,所以它是 view。 foo.pl 中将它们粘合在一起的主程序控制数据流。它是控制器。
触发整个事情的初始网站可以是程序的一部分,如果没有传递参数则显示,也可以是独立的 .html 文件。这取决于你。
所有这些都可以通过普通的旧 CGI 实现。您不需要为此使用任何 Web 框架。但是随着您的应用程序的发展,您会发现现代框架让您的生活更轻松。
使用http://asciiflow.com/创建的图表