如何重构使用 Template Toolkit 和 DBI 的 Perl 代码以利用 FastCGI?
背景
下面是一段典型的 Perl 代码(为了讨论而sample.pl),它使用 CGI 获取提交的表单数据,将表单数据传递给 DBI,然后 DBI 从 MySQL 检索所需的行并然后将结果交给 Template Toolkit 以呈现为 HTML 文档进行显示。
sample.pl 的代码清单:
#!/usr/bin/perl
use strict;
use CGI;
use DBI:
use Template;
#Grab submitted form data
my $cgi = CGI->new();
my $idFromSomewhere= $cgi->param('id');
my $driver = "mysql";
my $server = "localhost:3306";
my $database = "test";
my $url = "DBI:$driver:$database:$server";
my $user = "apache";
my $password = "";
#Connect to database
my $db_handle = DBI->connect( $url, $user, $password )
or die $DBI::errstr;
#SQL query to execute
my $sql = "SELECT * FROM tests WHERE id=?";
#Prepare SQL query
my $statement = $db_handle->prepare($sql)
or die "Couldn't prepare query '$sql': $DBI::errstr\n";
#Execute SQL Query
$statement->execute($idFromSomewhere)
or die "Couldn't execute query '$sql': $DBI::errstr\n";
#Get query results as hash
my $results = $statement->fetchall_hashref('id');
$db_handle->disconnect();
my $tt = Template->new();
#HTML output template
my $input = 'template.html';
my $vars = {
tests => $results,
};
#Process template and output as HTML
$tt->process($input, $vars)
or die $tt->error();
为了获得更好的性能和可扩展性,提供共享服务器的 Web 主机(例如 Dreamhost)强烈建议所有生产 Perl 脚本都支持 FastCGI。 FastCGI 文档非常清楚地说明了如何修改现有 Perl 代码以支持 FastCGI。 下面的简单代码通常作为示例给出:
use FCGI;
while (FCGI::accept >= 0)
{
#Run existing code.
}
不太清楚的是 while 循环中的位置和内容。
子问题
A. > Sample.pl 中的代码是否应该简单地围绕现有代码,如下所示:
while (FCGI::accept >= 0)
{
#Grab submitted form data
my $cgi = CGI->new();
...
...
#Process template and output as HTML
$tt->process($input, $vars)
or die $tt->error();
}
B. 或者还有更多的事情吗? 例如,处理 cgi、数据库和模板的代码是否应该重构为它们自己的子程序?
C. 应该在 FCGI while 循环内部还是外部调用 DBI->connect() 和 $db_handle->disconnect() 以及对性能有何影响?
D. $tt->process() 应该在 FCGI while 循环内部还是外部调用?
Background
Below is a typical piece of Perl code (sample.pl for the sake of discussion) that grabs submitted form data using CGI, passes the form data to DBI which will then retrieve the required rows from MySQL and then hands the results over to Template Toolkit to render into a HTML document for display.
Code listing for sample.pl :
#!/usr/bin/perl
use strict;
use CGI;
use DBI:
use Template;
#Grab submitted form data
my $cgi = CGI->new();
my $idFromSomewhere= $cgi->param('id');
my $driver = "mysql";
my $server = "localhost:3306";
my $database = "test";
my $url = "DBI:$driver:$database:$server";
my $user = "apache";
my $password = "";
#Connect to database
my $db_handle = DBI->connect( $url, $user, $password )
or die $DBI::errstr;
#SQL query to execute
my $sql = "SELECT * FROM tests WHERE id=?";
#Prepare SQL query
my $statement = $db_handle->prepare($sql)
or die "Couldn't prepare query '$sql': $DBI::errstr\n";
#Execute SQL Query
$statement->execute($idFromSomewhere)
or die "Couldn't execute query '$sql': $DBI::errstr\n";
#Get query results as hash
my $results = $statement->fetchall_hashref('id');
$db_handle->disconnect();
my $tt = Template->new();
#HTML output template
my $input = 'template.html';
my $vars = {
tests => $results,
};
#Process template and output as HTML
$tt->process($input, $vars)
or die $tt->error();
For better performance and scalability, web hosts providing shared servers, such as Dreamhost, strongly recommend that all production Perl script support FastCGI. The FastCGI documentation is pretty clear on how to modify existing Perl code to support FastCGI. The simple code below is often given as an example:
use FCGI;
while (FCGI::accept >= 0)
{
#Run existing code.
}
What's not so clear is where and what to put in the while loop.
Sub Questions
A.
Should the code in sample.pl be simply wrapped around the existing code like so:
while (FCGI::accept >= 0)
{
#Grab submitted form data
my $cgi = CGI->new();
...
...
#Process template and output as HTML
$tt->process($input, $vars)
or die $tt->error();
}
B.
Or is there more to it? For instance, should the code that handles the cgi, database and template be refactored into their own subs?
C.
Should DBI->connect() and $db_handle->disconnect() be called inside or outside the FCGI while loop and what are the performance implications?
D.
Should $tt->process() be called inside or outside the FCGI while loop?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果您熟悉 CGI.pm,则使用 FCGI.pm 没有意义,请使用 CGI::Fast。
您转换为使用 CGI::Fast 的示例将是:
至于您的子问题:
除非您 100% 确定您知道自己在做什么,否则不要使用 FCGI。 你肯定想要 CGI::Fast :)
我会重构它以提高可读性
顺便说一句 - 如果您想用 Perl 开发网站,至少看看 Catalyst (http:// www.catalystframework.org/)
If you're familiar with CGI.pm, there is no point in using FCGI.pm, use CGI::Fast.
Your example converted to use CGI::Fast would be:
As for your sub questions:
Do not use FCGI unless you are 100% sure that you know what you're doing. You definitely want CGI::Fast :)
I would refactor it for readability
Just as a side information - if you want to develop websites in Perl, at least take a look at Catalyst (http://www.catalystframework.org/)
如果您想使用 FCGI,则只需在该循环中执行最少的操作即可启动任务。 其他所有内容都应该存在于模块中,您所需要做的就是传递输入。
其余的东西都在 MyApplication 的某个地方。 任何有趣的内容都不应该出现在 FastCGI 脚本中。 您不知道如何将所有应用程序内容与激活它的内容紧密耦合。
你可能想看看我在 掌握 Perl 中关于 modulinos 的章节,看看如何将你的脚本变成可重复使用的模块。 诸如此类的事情让事情变得非常容易。
对于持久数据库连接,您还有更多的工作要做。 您可以在环路外启动连接,但您需要定期对其进行 ping 操作,或许还需要重新建立连接。 请参阅 Apache::DBI 来了解这一点。
If you want to use FCGI, then only do the bare minimum in that loop to start the task. Everything else should live in modules, and all you need to do is pass the input along.
The rest of the stuff is in MyApplication somewhere. Anything interesting shouldn't be in the FastCGI script. You don't what to tightly couple all the application stuff with the thing that's activating it.
You might want to see my chapter on modulinos in Mastering Perl to see how you can turn your scripts into re-usable modules. That sort of thing makes stuff like this really easy.
For the persistent database connections, you have a little bit more work to do. You can start a connection outside the loop, but periodicially you need to ping it and perhaps re-establish it. See what Apache::DBI for this.
子问题 C:(持久数据库连接)
看看 DBI->connect_cached()。 我相信您可以在 CGI::Fast 循环中内部使用它,并且 DBI.pm 会记住/缓存您的连接。 因此,在第二次、第三次等使用相同参数调用 connect_cached() 时,您将恢复现有连接。 如果旧连接不再可用,它将创建一个新连接。
这种方法的真正好处是,您必须对现有应用程序进行的唯一更改(除了添加 CGI::Fast 循环)就是用 connect_cached() 替换 connect()。 connect_cached() 也适用于普通 CGI。
另请参阅我是否必须将数据库连接/初始化置于 FCGI 循环之外才能利用 Perl 中的 FastCGI?
和
http://www.mail-archive.com/[电子邮件受保护]/msg04351.html
Subquestion C: (persistent DB connections)
Take a look at DBI->connect_cached(). I believe you can use it inside your CGI::Fast loop, and DBI.pm will remember/cache your connection. So, on the 2nd, 3rd, etc. calls to connect_cached() with the same parameters, you will get back an existing connection. It will create a new connection if the old one is no longer available.
What's really nice about this approach is that the only change you have to make to your existing application (other than adding the CGI::Fast loop) is to replace connect() with connect_cached(). And connect_cached() will work with plain vanilla CGI too.
See also Do I have to put DB connection/initialization outside of the FCGI loop to take advantage of FastCGI in Perl?
and
http://www.mail-archive.com/[email protected]/msg04351.html