如何管理自己的 SIGINT 和 SIGTERM 信号?
how to manage myself SIGINT and SIGTERM signals?
我正在开发一个简单的基于 Mojolicious::Lite
的服务器,其中包含一个 websocket 端点。
我想处理一些终止信号以正常终止 websocket 连接并避免客户端(java 应用程序)出现异常。
我尝试定义我的信号处理程序,就像我以前使用 HTTP::Daemon
的服务器一样。
问题是它们似乎被忽略了。也许在Mojolicious层重新定义了,我还没有找到任何参考资料。
我期待看到我的终止消息,但它并没有发生
[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
当服务器在终端中处于前台时,我通过输入 Ctrl-C
直接发送 SIGINT
,我可以优雅地终止服务器(例如,当由 cron 或其他无显示方式启动时) kill <pid>
.
在以前的一些服务器中,我试图通过处理来非常详尽:
HUP
现在用来重新加载配置的劫持信号
SIGINT
Ctrl-C
SIGQUIT
Ctrl-\
SIGABRT
例如库异常终止
SIGTERM
外部终止请求-"friendly"kill
(遭到野蛮kill -9
的反对
TSTP
使用 Ctrl-Z 暂停
CONT
当使用 fg
或 bg
从 Ctrl-Z 恢复时
所有这些处理程序都允许优雅地退出并清理资源,确保数据一致性或在外部更改后重新加载配置或数据模型,具体取决于程序和需要。
我找到了包 Mojo::IOLoop::Signal
,« 非阻塞信号处理程序 » 但它似乎是另一回事。错了?
这是我的简化代码(使用简单的 perl ws_store_test.pl daemon
运行):
文件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 和 SIGTERM 处理程序在服务器启动时重新定义。在 morbo 中是:
local $SIG{INT} = local $SIG{TERM} = sub {
$self->{finished} = 1;
kill 'TERM', $self->{worker} if $self->{worker};
};
local $SIG{INT} = local $SIG{TERM} = sub { $loop->stop };
如果您自己在顶层重新定义 SIGINT/SIGTERM 的处理程序,那些 local
将覆盖它们。相反,我的建议是在 before_dispatch
挂钩中重新定义它们一次。例如:
sub add_sigint_handler {
my $old_int = $SIG{INT};
$SIG{INT} = sub {
say "SIGINT"; # to be sure to display something
app->log->info("SIGINT / CTRL-C received. Leaving...");
terminate_clients;
$old_int->(); # Calling the old handler to cleanly exit the server
}
}
app->hook(before_dispatch => sub {
state $unused = add_sigint_handler();
});
这里我使用 state
来确保 add_sigint_handler
只被计算一次(因为如果它被计算多次,$old_int
在之后就不会有正确的值第一次)。另一种写法可能是:
my $flag = 0;
app->hook(before_dispatch => sub {
if ($flag == 0) {
add_sigint_handler();
$flag = 1;
}
});
或者,
app->hook(before_dispatch => sub {
state $flag = 0;
if ($flag == 0) {
add_sigint_handler();
$flag = 1;
}
});
我正在开发一个简单的基于 Mojolicious::Lite
的服务器,其中包含一个 websocket 端点。
我想处理一些终止信号以正常终止 websocket 连接并避免客户端(java 应用程序)出现异常。
我尝试定义我的信号处理程序,就像我以前使用 HTTP::Daemon
的服务器一样。
问题是它们似乎被忽略了。也许在Mojolicious层重新定义了,我还没有找到任何参考资料。
我期待看到我的终止消息,但它并没有发生
[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
当服务器在终端中处于前台时,我通过输入 Ctrl-C
直接发送 SIGINT
,我可以优雅地终止服务器(例如,当由 cron 或其他无显示方式启动时) kill <pid>
.
在以前的一些服务器中,我试图通过处理来非常详尽:
HUP
现在用来重新加载配置的劫持信号SIGINT
Ctrl-CSIGQUIT
Ctrl-\SIGABRT
例如库异常终止SIGTERM
外部终止请求-"friendly"kill
(遭到野蛮kill -9
的反对
TSTP
使用 Ctrl-Z 暂停CONT
当使用fg
或bg
从 Ctrl-Z 恢复时
所有这些处理程序都允许优雅地退出并清理资源,确保数据一致性或在外部更改后重新加载配置或数据模型,具体取决于程序和需要。
我找到了包 Mojo::IOLoop::Signal
,« 非阻塞信号处理程序 » 但它似乎是另一回事。错了?
这是我的简化代码(使用简单的 perl ws_store_test.pl daemon
运行):
文件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 和 SIGTERM 处理程序在服务器启动时重新定义。在 morbo 中是:
local $SIG{INT} = local $SIG{TERM} = sub {
$self->{finished} = 1;
kill 'TERM', $self->{worker} if $self->{worker};
};
local $SIG{INT} = local $SIG{TERM} = sub { $loop->stop };
如果您自己在顶层重新定义 SIGINT/SIGTERM 的处理程序,那些 local
将覆盖它们。相反,我的建议是在 before_dispatch
挂钩中重新定义它们一次。例如:
sub add_sigint_handler {
my $old_int = $SIG{INT};
$SIG{INT} = sub {
say "SIGINT"; # to be sure to display something
app->log->info("SIGINT / CTRL-C received. Leaving...");
terminate_clients;
$old_int->(); # Calling the old handler to cleanly exit the server
}
}
app->hook(before_dispatch => sub {
state $unused = add_sigint_handler();
});
这里我使用 state
来确保 add_sigint_handler
只被计算一次(因为如果它被计算多次,$old_int
在之后就不会有正确的值第一次)。另一种写法可能是:
my $flag = 0;
app->hook(before_dispatch => sub {
if ($flag == 0) {
add_sigint_handler();
$flag = 1;
}
});
或者,
app->hook(before_dispatch => sub {
state $flag = 0;
if ($flag == 0) {
add_sigint_handler();
$flag = 1;
}
});