Curiously, I have been using Apache::Reload a lot lately and it has
been working really well most of the time.

I'm wondering if maybe another way to deal with troublesome module
reloads is to subclass Apache::Reload and override the reload process
for those specific namespaces.

On Thu, May 27, 2010 at 8:11 AM, Michael Schout <msch...@gkg.net> wrote:
> 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