Почему горячее развертывание 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;
}