如何管理自己的SIGINT和SIGTERM信号?

Sek*_*eki 5 perl signals mojolicious mojolicious-lite

我正在开发一个Mojolicious::Lite基于简单的服务器,其中包括一个 websocket 端点。

\n\n

我想处理一些终止信号以优雅地终止 websocket 连接并避免客户端(java 应用程序)中出现异常。

\n\n

我尝试使用 来定义我的信号处理程序,就像我以前使用的服务器一样HTTP::Daemon。\n问题是它们似乎被忽略了。也许是在 Mojolicious 层中重新定义的,我还没有找到任何关于它的参考。

\n\n

我期待看到我的终止消息,但它没有发生

\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-Ckill <pid>

\n\n

在以前的一些服务器中,我试图通过处理来做到非常详尽:

\n\n
    \n
  • HUP现在使用劫持信号来重新加载配置
  • \n
  • SIGINTCtrl-C
  • \n
  • SIGQUITCtrl-\\
  • \n
  • SIGABRT例如异常库终止
  • \n
  • SIGTERM外部终止请求 - “友好” kill(遭到残酷的反对kill -9
  • \n
  • TSTP使用 Ctrl-Z 暂停
  • \n
  • CONTfg当使用或从 Ctrl-Z 恢复时bg
  • \n
\n\n

所有这些处理程序都允许优雅地退出并清理资源,确保数据一致性或在外部更改后重新加载配置或数据模型,具体取决于程序和需求。

\n\n

我找到了包Mojo::IOLoop::Signal\xc2\xab 一个非阻塞信号处理程序 \xc2\xbb 但它似乎是不同的东西。错误的?

\n\n

这是我的简化代码(使用简单的运行perl ws_store_test.pl daemon):

\n\n

文件 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

Dad*_*ada 6

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)