Sek*_*eki 5 perl signals mojolicious mojolicious-lite
我正在开发一个Mojolicious::Lite
基于简单的服务器,其中包括一个 websocket 端点。
我想处理一些终止信号以优雅地终止 websocket 连接并避免客户端(java 应用程序)中出现异常。
\n\n我尝试使用 来定义我的信号处理程序,就像我以前使用的服务器一样HTTP::Daemon
。\n问题是它们似乎被忽略了。也许是在 Mojolicious 层中重新定义的,我还没有找到任何关于它的参考。
我期待看到我的终止消息,但它没有发生
\n\n[Mon Mar 23 14:01:28 2020] [info] Listening at "http://*:3000"\nServer available at http://127.0.0.1:3000\n^C # <-- i want to see my signal received message here if type Ctrl-c\n
Run Code Online (Sandbox Code Playgroud)\n\n当服务器位于终端前台时,我通过输入直接发送,并且我可以SIGINT
使用.Ctrl-C
kill <pid>
在以前的一些服务器中,我试图通过处理来做到非常详尽:
\n\nHUP
现在使用劫持信号来重新加载配置SIGINT
Ctrl-CSIGQUIT
Ctrl-\\SIGABRT
例如异常库终止SIGTERM
外部终止请求 - “友好” kill
(遭到残酷的反对kill -9
TSTP
使用 Ctrl-Z 暂停CONT
fg
当使用或从 Ctrl-Z 恢复时bg
所有这些处理程序都允许优雅地退出并清理资源,确保数据一致性或在外部更改后重新加载配置或数据模型,具体取决于程序和需求。
\n\n我找到了包Mojo::IOLoop::Signal
\xc2\xab 一个非阻塞信号处理程序 \xc2\xbb 但它似乎是不同的东西。错误的?
这是我的简化代码(使用简单的运行perl ws_store_test.pl daemon
):
文件 ws_store_test.pl
\n\n# Automatically enables "strict", "warnings", "utf8" and Perl 5.10 features\nuse Mojolicious::Lite;\n\nmy $store = {};\nmy $ws_clients = {};\n\nsub terminate_clients {\n for my $peer (keys %$ws_clients){\n $ws_clients->{$peer}->finish;\n }\n}\n\n$SIG{INT} = sub {\n say "SIGINT"; # to be sure to display something\n app->log->info("SIGINT / CTRL-C received. Leaving...");\n terminate_clients;\n};\n$SIG{TERM} = sub {\n say "SIGTERM"; # to be sure to display something\n app->log->info("SIGTERM - External termination request. Leaving...");\n terminate_clients;\n};\n\n# this simulates a change on datamodel and notifies the clients\nsub update_store {\n my $t = localtime time;\n $store->{last_time} = $t;\n for my $peer (keys %$ws_clients){\n app->log->debug(sprintf \'notify %s\', $peer);\n $ws_clients->{$peer}->send({ json => $store\n });\n }\n}\n\n# Route with placeholder - to test datamodel contents\nget \'/:foo\' => sub {\n my $c = shift;\n my $foo = $c->param(\'foo\');\n $store->{$foo}++;\n $c->render(text => "Hello from $foo." . (scalar keys %$store ? " already received " . join \', \', sort keys %$store : "") );\n};\n\n# websocket service with optional parameter\nwebsocket \'/ws/tickets/*id\' => { id => undef } => sub {\n my $ws = shift;\n my $id = $ws->param(\'id\');\n\n my $peer = sprintf \'%s\', $ws->tx;\n app->log->debug(sprintf \'Client connected: %s, id=%s\', $peer, $id);\n $ws_clients->{$peer} = $ws->tx;\n $store->{$id} = {};\n\n $ws->on( message => sub {\n my ($c, $message) = @_;\n app->log->debug(sprintf \'WS received %s from a client\', $message);\n });\n\n $ws->on( finish => sub {\n my ($c, $code, $reason) = @_;\n app->log->debug(sprintf \'WS client disconnected: %s - %d - %s\', $peer, $code, $reason);\n delete $ws_clients->{$peer};\n });\n};\n\nplugin Cron => ( \'* * * * *\' => \\&update_store );\n\n# Start the Mojolicious command system\napp->start;\n
Run Code Online (Sandbox Code Playgroud)\n
SIGINT 和 SIGTERM 处理程序在服务器启动时重新定义。在莫尔博中,这是:
local $SIG{INT} = local $SIG{TERM} = sub {
$self->{finished} = 1;
kill 'TERM', $self->{worker} if $self->{worker};
};
Run Code Online (Sandbox Code Playgroud)
在Mojo::Server::Daemon中,这是:
local $SIG{INT} = local $SIG{TERM} = sub { $loop->stop };
Run Code Online (Sandbox Code Playgroud)
如果您自己在顶层重新定义 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();
});
Run Code Online (Sandbox Code Playgroud)
在这里,我使用它state
来确保add_sigint_handler
仅评估一次(因为如果评估多次,则$old_int
在第一次后将不会有正确的值)。另一种写法可能是:
my $flag = 0;
app->hook(before_dispatch => sub {
if ($flag == 0) {
add_sigint_handler();
$flag = 1;
}
});
Run Code Online (Sandbox Code Playgroud)
或者,
app->hook(before_dispatch => sub {
state $flag = 0;
if ($flag == 0) {
add_sigint_handler();
$flag = 1;
}
});
Run Code Online (Sandbox Code Playgroud)