I am working on a simple Mojolicious::Lite
based server that includes a websocket end point.
I would like to handle some termination signals to terminate gracefully the websocket connections and avoid exceptions in the clients (a java application).
I have tried to define my signal handlers like I am used to with my previous servers using HTTP::Daemon
.
The problem is that they seem to be ignored. Perhaps redefined in the Mojolicious layer, I did not found any reference on it yet.
I am expecting to see my termination message, but it does not happen
[Mon Mar 23 14:01:28 2020] [info] Listening at "http://*:3000"
Server available at http://127.0.0.1:3000
^C # <-- i want to see my signal received message here if type Ctrl-c
I am sending SIGINT
directly by entering Ctrl-C
when the server is in foreground in the terminal, and I can terminate gracefully the server (e.g. when started by a cron or other displayless mean) with a kill <pid>
.
In some previous servers I tried to be quite exaustive by handling:
HUP
hijacked signal used nowadays to reload configSIGINT
Ctrl-CSIGQUIT
Ctrl-\SIGABRT
e.g abnormal library terminationSIGTERM
external termination request - "friendly"kill
(by opposition of brutalkill -9
TSTP
suspend with Ctrl-ZCONT
when resuming from Ctrl-Z withfg
orbg
All these handlers allow to exit gracefully with cleaning resources, ensuring data consistency or reload configuration or data models after external change, depending on the program and the needs.
I have found the package Mojo::IOLoop::Signal
, « a Non-blocking signal handler » but it seems to be a different thing. Wrong?
Here is my simplified code (runs with a simple perl ws_store_test.pl daemon
):
File ws_store_test.pl
# Automatically enables "strict", "warnings", "utf8" and Perl 5.10 features
use Mojolicious::Lite;
my $store = {};
my $ws_clients = {};
sub terminate_clients {
for my $peer (keys %$ws_clients){
$ws_clients->{$peer}->finish;
}
}
$SIG{INT} = sub {
say "SIGINT"; # to be sure to display something
app->log->info("SIGINT / CTRL-C received. Leaving...");
terminate_clients;
};
$SIG{TERM} = sub {
say "SIGTERM"; # to be sure to display something
app->log->info("SIGTERM - External termination request. Leaving...");
terminate_clients;
};
# this simulates a change on datamodel and notifies the clients
sub update_store {
my $t = localtime time;
$store->{last_time} = $t;
for my $peer (keys %$ws_clients){
app->log->debug(sprintf 'notify %s', $peer);
$ws_clients->{$peer}->send({ json => $store
});
}
}
# Route with placeholder - to test datamodel contents
get '/:foo' => sub {
my $c = shift;
my $foo = $c->param('foo');
$store->{$foo}++;
$c->render(text => "Hello from $foo." . (scalar keys %$store ? " already received " . join ', ', sort keys %$store : "") );
};
# websocket service with optional parameter
websocket '/ws/tickets/*id' => { id => undef } => sub {
my $ws = shift;
my $id = $ws->param('id');
my $peer = sprintf '%s', $ws->tx;
app->log->debug(sprintf 'Client connected: %s, id=%s', $peer, $id);
$ws_clients->{$peer} = $ws->tx;
$store->{$id} = {};
$ws->on( message => sub {
my ($c, $message) = @_;
app->log->debug(sprintf 'WS received %s from a client', $message);
});
$ws->on( finish => sub {
my ($c, $code, $reason) = @_;
app->log->debug(sprintf 'WS client disconnected: %s - %d - %s', $peer, $code, $reason);
delete $ws_clients->{$peer};
});
};
plugin Cron => ( '* * * * *' => \&update_store );
# Start the Mojolicious command system
app->start;
SIGINT
by usingCtrl-C
while the server is in foreground, forSIGTERM
it can be viakill <pid>
. I will update my question. – Sexy