Как меня убить, используя Perl FCGI? - PullRequest
8 голосов
/ 27 февраля 2010

У меня небольшая проблема с nginx и модулем Perl FCGI. У меня есть длинная операция в моей программе FCGI, которая может пережить сервер (или пользователя на сервере) на другом конце сокета Unix, который я использую для связи FCGI. Мне нужен цикл FCGI accept () в моей программе, чтобы разорвать, если запрос FCGI закрыт. Я попытался установить обработчики сигналов INT, TERM и т. Д., Но они ничего не делают, поскольку единственное взаимодействие между nginx и моей программой происходит через сокет FCGI, AFAIK.

Я также пытался это , но я не вижу способа использовать модуль FCGI в Perl для отправки необработанных данных в или из nginx через сокет FCGI. Есть ли способ, которым я могу сделать это без изменения модуля FCGI, чтобы иметь функцию "ping"?

Основная проблема в том, что моя программа не знает, завершил ли nginx запрос FCGI.

Пример:

#!/usr/bin/perl -w
use strict;
use FCGI;

my $fcgi_socket = FCGI::OpenSocket( '/tmp/test.socket', 100000 );
my $request = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%ENV, $fcgi_socket);
REQUEST: while($request->Accept() >= 0) {
    #begin handling request
    my $result = '';
    while (1) { #or select(), etc
        if (somehow check whether the fcgi $request is still live) {
            next REQUEST;
        }
        #check for results, set $result if done
    }
    print $result;
}

1 Ответ

1 голос
/ 09 марта 2010

Вы должны использовать FCGI реализацию, которая обрабатывает FCGI_ABORT_REQUEST.

Вы не можете использовать следующее, потому что они игнорируют FCGI_ABORT_REQUEST:

Вы могли бы использовать следующее, которое обрабатывает FCGI_ABORT_REQUEST:

При использовании AnyEvent-FCGI проверка прерванного запроса так же проста, как и вызов $request->is_active(), но имейте в виду, что is_active() не будет отражать истинное состояние запроса, пока не вернется обработчик on_request, что означает вы должны вернуться из on_request как можно скорее и как-то выполнить реальную работу "параллельно" (вы, вероятно, не хотите использовать потоки Perl , но что-то более похожее на продолжения ), чтобы дать циклу AnyEvent возможность обрабатывать любые дальнейшие запросы (включая FCGI_ABORT_REQUEST с), пока вы выполняете длительные операции.

Я недостаточно знаком с AnyEvent, чтобы точно знать, есть ли лучший способ сделать это, но вот мой пример, ниже, для начала:

use AnyEvent;
use AnyEvent::FCGI;

my @jobs;
my $process_jobs_watcher;

sub process_jobs {
  # cancel aborted jobs
  @jobs = grep {
    if ($_->[0]->is_active) {
      true
    } else {
      # perform any job cleanup
      false
    }
  } @jobs;
  # any jobs left?
  if (scalar(@jobs)) {
    my $job = $jobs[0];
    my ( $job_request, $job_state ) = @$job;
    # process another chunk of $job
    #  if job is done, remove (shift) from @jobs
  } else {
    # all jobs done; go to sleep until next job request
    undef $process_jobs_watcher;
  }
}

my $fcgi = new AnyEvent::FCGI(
  port => 9000,
  on_request => sub {
    my $request = shift;
    if (scalar(@jobs) < 5) { # set your own limit
      # accept request and send back headers, HTTP status etc.
      $request.print_stdout("Content-Type: text/plain\nStatus: 200 OK\n\n");
      # This will hold your job state; can also use Continutiy
      #  http://continuity.tlt42.org/
      my $job_state = ...;
      # Enqueue job for parallel processing:
      push @jobs, [ $request, $job_state ];
      if (!$process_jobs_watcher) {
        # If and only if AnyEvent->idle() does not work,
        #  use AnyEvent->timer() and renew from process_jobs
        $process_jobs_watcher = AnyEvent->idle(cb => \&process_jobs);
      }
    } else {
      # refuse request
      $request.print_stdout("Content-Type: text/plain\nStatus: 503 Service Unavailable\n\nBusy!");
    }
  }
);

AnyEvent->loop;
...