Почему горячее развертывание Hypnotoad перезапускает старые http-запросы?

В двух словах:
Когда я выполняю горячее развертывание Hypnotoad, иногда новый сервер немедленно обрабатывает множество HTTP-запросов, которые уже были обработаны предыдущим сервером.

Если ответ был обработан, но поток все еще выполняет некоторую обработку, Mojo/Hypnotoad удерживает запрос до тех пор, пока обработка не будет остановлена? Нужно ли сообщать серверу, что HTTP-запрос разрешен?

Длинная версия:
У меня есть приложение Mojolicious::Lite, работающее под Hypnotoad. Функция приложения - принимать HTTP-запросы от другого сервиса.

Мы обрабатываем задания, которые проходят через ряд государств. При каждом изменении состояния задания приложение уведомляется с помощью HTTP-запроса. Это небольшой загруженный скрипт - получение более 1000 запросов в час.

Работа скриптов состоит в том, чтобы манипулировать некоторыми данными... делать обновления БД, редактировать файлы, отправлять почту.

В стремлении продолжать работу, когда он получает HTTP-запрос, он проверяет правильность полученных данных. Если данные выглядят хорошо, они немедленно отправляют вызывающему ответ 200, а затем продолжают выполнять более трудоемкие задачи. (Я предполагаю, что это основная причина)

Когда я выполняю горячее развертывание - путем повторного запуска сценария запуска (который запускает 'localperl/bin/hypnotoad $RELDIR/etc/bki/bki.pl') - некоторые запросы, которые уже были обработаны, отправляются на новый сервер и обрабатываются повторно.

Почему эти старые транзакции все еще удерживаются исходным сервером? Многие были уже давно завершены! Нужно ли сообщать Mojolicious, что запрос выполняется до того, как он сработает и будет портить данные? (Я рассмотрел $c->finish(), но это только для сокетов?) Как Hypnotoad решает, какие запросы следует передавать на сервер-заменитель?

Вот код psuedo с тем, что я делаю:

get '/jobStateChange/:jobId/:jobState/:jobCause' => sub {
    my $c =shift;

    my $jobId = $c->stash("jobId");
    return $c->render(text => "invalid jobId: $jobId", status => 400) unless $jobId=~/^\d+$/;

    my $jobState = $c->stash("jobState");
    return $c->render(text => "invalid jobState: $jobState", status => 400) unless $jobState=~/^\d+$/;

    my $jobCause = $c->stash("jobCause");
    return $c->render(text => "invalid jobCause: $jobCause", status => 400) unless $jobCause=~/^\d+$/;

    my $jobLocation = $c->req->param('jobLocation');
    if ($jobLocation){ $jobLocation = $ENV{'DATADIR'} . "/jobs/" . $jobLocation; }
    unless ( $jobLocation && -d $jobLocation ){
        app->log->debug("determining jobLocation because passed job jobLocation isn't useable");
        $jobLocation = getJobLocation($jobId);
        $c->stash("jobLocation", $jobLocation);
    }

    # TODO - more validation? would BKI lie to us?
    return if $c->tx->res->code && 400 == $c->tx->res->code; # return if we rendered an error above

    # tell BKI we're all set ASAP
    $c->render(text => 'ok');
    handleJobStatusUpdate($c, $jobId, $jobState, $jobCause, $jobLocation);

};

sub handleJobStatusUpdate{
    my ($c, $jobId, $jobState, $jobCause, $jobLocation) = @_;
    app->log->info("job $jobId, state $jobState, cause $jobCause, loc $jobLocation");

    # set the job states in jobs
    app->work_db->do($sql, undef, @params);


    if ($jobState == $SOME_JOB_STATE) { 
        ... do stuff ...
        ... uses $c->stash to hold data used by other functions
    }

    if ($jobState == $OTHER_JOB_STATE) { 
        ... do stuff ...
        ... uses $c->stash to hold data used by other functions
    }
}

1 ответ

Решение

Ваш запрос не будет завершен, пока не вернется обработчик запроса. Это маленькое приложение, например, потребует 5 секунд для вывода "test":

# test.pl
use Mojolicious::Lite;
get '/test' => sub { $_[0]->render( text => "test" ); sleep 5 };
app->start;

Обходной путь для вашего приложения - запуск handleJobStatusUpdate в фоновом процессе.

get '/jobStateChange/:jobId/:jobState/:jobCause' => sub {
    my $c =shift;    
    my $jobId = $c->stash("jobId");
    my $jobState = $c->stash("jobState");
    my $jobCause = $c->stash("jobCause");
    my $jobLocation = $c->req->param('jobLocation');
    ...
    $c->render(text => 'ok');
    if (fork() == 0) {
        handleJobStatusUpdate($c, $jobId, $jobState, $jobCause, $jobLocation);
        exit;
    }
Другие вопросы по тегам