On 09/11/2009 04:26 PM, Jonathan Swartz wrote:
> I'm thinking about an improved solution to recognizing module changes in
> a running server, without restarting the server.

This thread is quite old, but it inspired me to implement a similar
strategy for dealing with module changes under mod_perl.

In my case, I was only really interested in restarting the server when
modules under a certain namespace changed (all of my app's modules are
under a single namespace, "My" for example).

My solution involved forking off a watcher process when the server
starts up.  This watcher processes uses File::ChangeNotify to watch for
changes in the given modules.  A nice benefit of using ::ChangeNotify,
is if you are on Linux, and have the Linux::Inotify2 modules installed,
then this happens with no cpu overhead, and changes are seen almost
instantaneous.  Once a change happens, my watcher processes detaches
from apache, and reloads it (retrying some configured number of times in
case there is a syntax error on the first change).

To do this, I created a module My::Apache2::Reload.  I register the
handler in httpd.conf as:

PerlPostConfigHandler My::Apache2::Reload

When apache starts up, it immediately restarts itself, so my handler has
to arrange to only start the watcher subprocess one time.  I do this on
the second restart like this:

> sub handler : method {
>     return Apache2::Const::OK unless Apache2::ServerUtil::restart_count() == 
> 2;
> 
>     start_watcher();
> 
>     return Apache2::Const::OK;
> }

start_watcher is implemented as follows:

> sub start_watcher {
>     unless (fork) { # child
>         require File::ChangeNotify;
> 
>         my $module_dir = '/path/to/my/modules';
>         my $config_dir = '/path/to/my/configs';
> 
>         my $watcher = File::ChangeNotify->instantiate_watcher(
>             directories => [$module_dir, $config_dir],
>             filter      => qr/\.(?:pm|conf|yml)$/);
> 
>         while (my @events = $watcher->wait_for_events) {
>             my @event_types = map { $_->type } @events;
>             if (any(@event_types) eq 'modify') {
>                 restart_apache();
>             }
>         }
>     }
> }

And restart_apache() is called when something that is watched changes:

> sub restart_apache {
>     disconnect_from_apache();
> 
>     require IPC::System::Simple;
> 
>     for my $attempt (1 .. $MaxRestartTries) {
>         if ($attempt > 1) {
>             sleep $RestartDelay;
>         }
> 
>         eval {
>             IPC::System::Simple::system('apache restart command');
>             CORE::exit(0);
>         };
>     }
> 
>     # give up.
>     CORE::exit(0);
> }

The disconnect_from_apache() is necessary because the watcher gets
killed off if you do not close all open filehandles in the child when
the restart is attempted.  Also we need to detach from the apache
process group.  This is done as follows:

> sub disconnect_from_apache {
>    POSIX::setsid();
> 
>    # close all open fds.
>    my $max_fds = POSIX::sysconf(&POSIX::_SC_OPEN_MAX) // 64;
> 
>    for my $fd (0 .. $max_fds) {
>        POSIX::close($fd);
>    }
> }


And thats it.  This has worked wonderfully for me.  Apache notices
changes in real time, and restarts happen quickly when developing.

If there is interest in a module like this, let me know and I will
package it up and put it on CPAN (obviously I wouldn't use the name
Apache2::Reload as that is taken already :)).

Regards,
Michael Schout

Reply via email to