【问题标题】:how to manage myself SIGINT and SIGTERM signals?如何管理自己的 SIGINT 和 SIGTERM 信号?
【发布时间】:2020-07-03 22:55:30
【问题描述】:

我正在开发一个基于 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,并且我可以使用kill &lt;pid&gt; 优雅地终止服务器(例如,当由cron 或其他无显示方式启动时)。

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

  • HUP 现在用来重新加载配置的劫持信号
  • SIGINTCtrl-C
  • SIGQUITCtrl-\
  • SIGABRT 例如异常库终止
  • SIGTERM 外部终止请求-“友好”kill(反对野蛮kill -9
  • TSTP 使用 Ctrl-Z 暂停
  • CONT 当使用 fgbg 从 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 发送到守护进程?
  • @HåkonHægland for SIGINT 在服务器处于前台时使用Ctrl-C,对于SIGTERM,它可以通过kill &lt;pid&gt;。我会更新我的问题。

标签: perl signals mojolicious mojolicious-lite


【解决方案1】:

SIGINT 和 SIGTERM 处理程序在服务器启动时重新定义。在morbo 这是:

local $SIG{INT} = local $SIG{TERM} = sub {
  $self->{finished} = 1;
  kill 'TERM', $self->{worker} if $self->{worker};
};

Mojo::Server::Daemon 这是:

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;
    }
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-19
    • 1970-01-01
    • 1970-01-01
    • 2016-11-09
    • 2020-05-11
    • 2023-04-02
    • 2016-01-20
    • 2013-12-21
    相关资源
    最近更新 更多