At server startup, before child processes are
spawned, you can do much more than just preload modules. You might
want to register code that will initialize a database connection for
each child when it is forked, tie read-only DBM files, fill in shared
caches, etc.
The startup.pl file is an ideal place to put
code that should be executed when the server starts. Once you have
prepared the code, load it in httpd.conf before
other mod_perl configuration directives with the
PerlRequire directive:
PerlRequire /home/httpd/perl/lib/startup.pl
Be careful with the startup file. Everything run at server
initialization is run with root privileges if
you start the server as root (which you have to
do unless you choose to run the server on an unprivileged port,
numbered 1024 or higher). This means that anyone who has write access
to a script or module that is loaded by
PerlModule, PerlRequire, or
<Perl>sections effectively has
root access to the system.
4.3.1. A Sample Startup File
Let's look at a
real-world startup file. The elements of the file are shown here,
followed by their descriptions.
use strict;
This pragma is worth using in every script longer than half a dozen
lines. It will save a lot of time and debugging later.
use lib qw(/home/httpd/lib /home/httpd/extra-lib);
This permanently adds extra directories to @INC,
something that's possible only during server
startup. At the end of each request's processing,
mod_perl resets @INC to the value it had after the
server startup. Alternatively, you can use the
PERL5LIB environment variable to add extra
directories to @INC.
$ENV{MOD_PERL} or die "not running under mod_perl!";
This is a sanity check. If mod_perl wasn't properly
built, the server startup is aborted.
use Apache::Registry ( );
use LWP::UserAgent ( );
use Apache::DBI ( );
use DBI ( );
Preload the
modules that get used by Perl code serving
requests. Unless you need the symbols (variables and subroutines)
exported by preloaded modules to accomplish something within the
startup file, don't import
them—it's just a waste of startup time and
memory. Instead, use the empty import list ( ) to
tell the import( ) function not to import anything.
use Carp ( );
$SIG{_ _WARN_ _} = \&Carp::cluck;
This is a useful snippet to enable extended warnings logged in the
error_log file. In addition to basic warnings, a
trace of calls is added. This makes tracking potential problems a
much easier task, since you know who called what.
The only drawback of this method is that it globally overrides the
default warning handler behavior—thus, in some places it might
be desirable to change the settings locally (for example, with
local $^W=0, or no warnings
under Perl 5.6.0 and higher). Usually warnings are turned off on
production machines to prevent unnecessary clogging of the
error_log file if your code is not very clean.
Hence, this method is mostly useful in a development environment.
use CGI ( );
CGI->compile(':all');
Some modules, such as CGI.pm, create their
subroutines at runtime via AUTOLOAD to improve
their loading time. This helps when the module includes many
subroutines but only a few are actually used. (Also refer to the
AutoSplit manpage.) Since the module is loaded
only once with mod_perl, it might be a good idea to precompile all or
some of its methods at server startup. This avoids the overhead of
compilation at runtime. It also helps share more compiled code
between child processes.
CGI.pm's compile(
) method performs this task. Note that
compile( ) is specific to
CGI.pm; other
modules that implement this feature may use another name for the
compilation method.
As with all modules we preload in the startup file, we
don't import symbols from them because they will be
lost when they go out of the file's scope.
The following code snippet makes sure that when the child process is
spawned, a connection to the database is opened automatically,
avoiding this performance hit on the first request:
Apache::DBI->connect_on_init
("DBI:mysql:database=test;host=localhost",
"user", "password", {
PrintError => 1, # warn( ) on errors
RaiseError => 0, # don't die on error
AutoCommit => 1, # commit executes immediately
}
);
The file ends with 1;so it can be successfully
loaded by Perl.
The entire
startup.pl file is shown
in Example 4-3.
Example 4-3. startup.pl
use strict;
use lib qw(/home/httpd/lib /home/httpd/extra-lib);
$ENV{MOD_PERL} or die "not running under mod_perl!";
use Apache::Registry ( );
use LWP::UserAgent ( );
use Apache::DBI ( );
use DBI ( );
use Carp ( );
$SIG{_ _WARN_ _} = \&Carp::cluck;
use CGI ( );
CGI->compile(':all');
Apache::DBI->connect_on_init
("DBI:mysql:database=test;host=localhost",
"user", "password", {
PrintError => 1, # warn( ) on errors
RaiseError => 0, # don't die on error
AutoCommit => 1, # commit executes immediately
}
);
1;
4.3.2. Syntax Validation
If the
startup
file doesn't include any modules that require the
mod_perl runtime environment during their loading, you can validate
its syntax with:
panic% perl -cw /home/httpd/perl/lib/startup.pl
The -c switch tells Perl to validate only the
file's syntax, and the -w
switch enables warnings.
Apache::DBI is an example of a module that cannot
be loaded outside of the mod_perl environment. If you try to load it,
you will get the following error message:
panic% perl -MApache::DBI -c -e 1
Can't locate object method "module" via package "Apache"
(perhaps you forgot to load "Apache"?) at
/usr/lib/perl5/site_perl/5.6.1/Apache/DBI.pm line 202.
Compilation failed in require.
BEGIN failed--compilation aborted.
However, Apache::DBI will work perfectly once
loaded from within mod_perl.
4.3.3. What Modules Should Be Added to the Startup File
Every module loaded at server startup will be shared
among the server children, saving a lot of RAM on your machine.
Usually, we put most of the code we develop into modules and preload
them.
You can even preload CGI scripts with
Apache::RegistryLoader, as explained in Chapter 10.
4.3.4. The Confusion with use( ) in the Server Startup File
Some people
wonder why they need to duplicate use Modulename
in the startup file and in the script itself. The confusion arises
due to misunderstanding use( ).
Let's take the POSIX module as an
example. When you write:
use POSIX qw(setsid);
use( ) internally performs two operations:
BEGIN {
require POSIX;
POSIX->import(qw(setsid));
}
The first operation loads and compiles the module. The second calls
the module's import( ) method and
specifies to import the symbol setsid into the
caller's namespace. The BEGIN
block makes sure that the code is executed as soon as possible,
before the rest of the code is even parsed. POSIX,
like many other modules, specifies a default export list. This is an
especially extensive list, so when you call:
use POSIX;
about 500 KB worth of symbols gets imported.
Usually, we don't need POSIX or
its symbols in the startup file; all we want is to preload it.
Therefore, we use an empty list as an argument for use(
):
use POSIX ( );
so the POSIX::import( ) method
won't be even called.
When we want to use the POSIX module in the code,
we use( ) it again, but this time no loading
overhead occurs because the module has been loaded already. If we
want to import something from the module, we supply the list of
symbols to load:
use POSIX qw(:flock_h);
This example loads constants used with the flock(
) function.
Technically, you aren't required to supply the
use( )statement in your handler code if the
module has already been loaded during server startup or elsewhere.
When writing your code, however, don't assume that
the module code has been preloaded. Someday in the future, you or
someone else will revisit this code and will not understand how it is
possible to use a module's methods without first
loading the module itself.
Please refer to the Exporter and
perlmod manpages, and to the section on
use( ) in the perlfunc
manpage for more information about import( ).
Remember that you can always use require( ) to
preload the files at server startup if you don't add
( ), because: