как мне управлять сигналами SIGINT и SIGTERM? - PullRequest
3 голосов
/ 23 марта 2020

Я работаю на простом Mojolicious::Lite сервере, который включает конечную точку веб-сокета.

Я хотел бы обработать некоторые сигналы завершения, чтобы корректно завершить соединения веб-сокета и избежать исключений в клиентах (a java application).

Я попытался определить свои обработчики сигналов так, как я привык на предыдущих серверах, используя 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

Я отправляю SIGINT напрямую, введя Ctrl-C, когда сервер находится на переднем плане в терминале, и я могу грациозно завершить работу сервера (например, при запуске cron или другим средним значением без отображения) с kill <pid>.

In На некоторых предыдущих серверах я пытался быть довольно исчерпывающим, обрабатывая:

  • HUP захваченный сигнал, используемый в настоящее время для перезагрузки конфигурации
  • SIGINT Ctrl- C
  • SIGQUIT Ctrl - \
  • SIGABRT например, ненормальное завершение работы библиотеки
  • SIGTERM запрос внешнего завершения - "дружественный" kill (противопоставление жестокому kill -9
  • TSTP приостановить с помощью Ctrl-Z
  • CONT при выходе из Ctrl-Z с помощью fg или bg

Все эти обработчики позволяют корректно завершить работу с чистящими ресурсами, обеспечивающими согласованность данных или перезагрузку конфигурации o • Модели данных после внешних изменений, в зависимости от программы и потребностей.

Я нашел пакет 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;

1 Ответ

3 голосов
/ 24 марта 2020

Обработчики 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;
    }
});
...