如何重构使用带有DBI的Template Toolkit的Perl代码来利用FastCGI?

时间:2009-06-04 08:42:20

标签: perl refactoring fastcgi dbi

背景

下面是使用CGI抓取提交的表单数据的典型Perl代码( sample.pl ),将表单数据传递给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循环中放置的位置和内容。

子问题

:一种。 应该将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();
}

或者还有更多吗?例如,处理cgi,数据库和模板的代码是否应该重构为它们自己的subs?

下进行。 应该DBI-> connect()和$ db_handle->在FCGI内部或外部调用disconnect()while循环以及性能影响是什么?

d 应该在循环时在FCGI内部还是外部调用$ tt-> process()?

3 个答案:

答案 0 :(得分:10)

如果您熟悉CGI.pm,使用FCGI.pm没有意义,请使用CGI :: Fast。

转换为使用CGI :: Fast的示例将是:

#!/usr/bin/perl
use strict;
use CGI::Fast;
use DBI;
use Template;

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;

while ( my $cgi = CGI::Fast->new() ) {

    #Grab submitted form data
    my $idFromSomewhere = $cgi->param( 'id' );

    #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' );

    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();
}

关于你的子问题:

  • A: 除非您100%确定自己知道自己在做什么,否则不要使用FCGI。你绝对想要CGI :: Fast:)
  • B: 为了便于阅读,我会重构它
  • C:如果您在接受连接之前使用DBI-> connect,那么您将获得永久数据库连接,从性能角度来看这是非常好的
  • D:绝对在里面。

作为附带信息 - 如果您想在Perl中开发网站,至少要看一下Catalyst(http://www.catalystframework.org/

答案 1 :(得分:1)

如果您想使用FCGI,那么只需在该循环中执行最低限度即可启动任务。其他所有东西都应该存在于模块中,你需要做的就是传递输入。

use FCGI;
while (FCGI::accept >= 0)
    {    
    MyApplication->activate( @args );
    }

剩下的东西都在MyApplication的某个地方。任何有趣的东西都不应该在FastCGI脚本中。你不需要将所有应用程序内容与激活它的东西紧密结合在一起。

您可能希望在Mastering Perl中查看关于modulinos的章节,了解如何将脚本转换为可重用的模块。这样的事情让这样的事情变得非常简单。

对于持久数据库连接,您还需要做更多工作。您可以在循环外部启动连接,但是您需要定期ping它并重新建立它。请参阅Apache::DBI了解这一点。

答案 2 :(得分:0)

子问题C :(持久数据库连接)

看看DBI->connect_cached()。我相信你可以使用里面你的CGI :: Fast循环,而DBI.pm会记住/缓存你的连接。因此,在使用相同参数调用connect_cached()的第2个,第3个等时,您将返回现有连接。如果旧连接不再可用,它将创建一个新连接。

这种方法的真正好处在于,您必须对现有应用程序进行的唯一更改(除了添加CGI :: Fast循环之外)是将connect()替换为connect_cached()。而且connect_cached()也可以使用普通的CGI。

另见Do I have to put DB connection/initialization outside of the FCGI loop to take advantage of FastCGI in Perl?http://www.mail-archive.com/cgiapp@lists.erlbaum.net/msg04351.html