многопоточное приложение FastCGI - PullRequest
8 голосов
/ 29 апреля 2011

Я хочу написать приложение FastCGI, которое должно обрабатывать несколько одновременных запросов с использованием потоков. Я посмотрел на образец threadaded.c , который поставляется с SDK:

#define THREAD_COUNT 20
static int counts[THREAD_COUNT];

static void *doit(void *a)
{
    int rc, i, thread_id = (int)a;
    pid_t pid = getpid();
    FCGX_Request request;
    char *server_name;

    FCGX_InitRequest(&request, 0, 0);

    for (;;)
    {
        static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
        static pthread_mutex_t counts_mutex = PTHREAD_MUTEX_INITIALIZER;

        /* Some platforms require accept() serialization, some don't.. */
        pthread_mutex_lock(&accept_mutex);
        rc = FCGX_Accept_r(&request);
        pthread_mutex_unlock(&accept_mutex);

        if (rc < 0)
            break;

        server_name = FCGX_GetParam("SERVER_NAME", request.envp);

        FCGX_FPrintF(request.out,…
        …     

        FCGX_Finish_r(&request);
    }

    return NULL;
}

int main(void)
{
    int i;
    pthread_t id[THREAD_COUNT];

    FCGX_Init();

    for (i = 1; i < THREAD_COUNT; i++)
        pthread_create(&id[i], NULL, doit, (void*)i);

    doit(0);

    return 0;
}

В спецификации FastCGI есть объяснение того, как веб-сервер будет определять, сколько соединений поддерживается приложением FastCGI:

Веб-сервер может запрашивать определенные переменные в приложении. сервер обычно выполняет запрос при запуске приложения, чтобы автоматизировать определенные аспекты системы конфигурации.

...

• FCGI_MAX_CONNS: максимальное количество одновременных транспортных соединений это приложение примет, например, "1" или "10".

• FCGI_MAX_REQS: максимальное количество одновременных запросов это приложение примет, например, «1» или "50".

• FCGI_MPXS_CONNS: «0», если это приложение не мультиплексирует соединения (т.е. обрабатывать одновременно запросы по каждому соединению), «1» в противном случае.

Но возвращаемые значения для этого запроса жестко кодируются в FastCGI SDK и возвращают 1 для FCGI_MAX_CONNS и FCGI_MAX_REQS и 0 для FCGI_MPXS_CONNS. Таким образом, образец threadaded.c никогда не получит несколько соединений.

Я протестировал образец с lighttpd и nginx, и приложение обрабатывало только один запрос одновременно. Как я могу получить свое приложение для обработки нескольких запросов? Или это неправильный подход?

Ответы [ 3 ]

6 голосов
/ 20 февраля 2013

Протестировал программу threadaded.c с http_load. Программа работает за nginx. Работает только один экземпляр программы. Если запросы обслуживаются последовательно, я ожидаю, что для 20 запросов потребуется 40 секунд, даже если они отправляются параллельно. Вот результаты (я использовал те же цифры, что и у Эндрю Брэдфорда - 20, 21 и 40) -

20 запросов, 20 параллельно, заняло 2 секунды -

$ http_load -parallel 20 -fetches 20 request.txt
20 fetches, 20 max parallel, 6830 bytes, in 2.0026 seconds
341.5 mean bytes/connection
9.98701 fetches/sec, 3410.56 bytes/sec
msecs/connect: 0.158 mean, 0.256 max, 0.093 min
msecs/first-response: 2001.5 mean, 2002.12 max, 2000.98 min
HTTP response codes:
  code 200 -- 20

21 запросов, 20 параллельно, заняло 4 секунды -

$ http_load -parallel 20 -fetches 21 request.txt
21 fetches, 20 max parallel, 7171 bytes, in 4.00267 seconds
341.476 mean bytes/connection
5.2465 fetches/sec, 1791.55 bytes/sec
msecs/connect: 0.253714 mean, 0.366 max, 0.145 min
msecs/first-response: 2001.51 mean, 2002.26 max, 2000.86 min
HTTP response codes:
  code 200 -- 21

40 запросов, 20 параллельно, заняло 4 секунды -

$ http_load -parallel 20 -fetches 40 request.txt
40 fetches, 20 max parallel, 13660 bytes, in 4.00508 seconds
341.5 mean bytes/connection
9.98732 fetches/sec, 3410.67 bytes/sec
msecs/connect: 0.159975 mean, 0.28 max, 0.079 min
msecs/first-response: 2001.86 mean, 2002.62 max, 2000.95 min
HTTP response codes:
  code 200 -- 40

Итак, это доказывает, что даже если значения FCGI_MAX_CONNS, FCGI_MAX_REQS и FCGI_MPXS_CONNS жестко заданы, запросы обслуживаются параллельно.

Когда Nginx получает несколько запросов, он помещает их все в очередь приложения FCGI вплотную. Он не ждет ответа от первого запроса перед отправкой второго запроса. В приложении FCGI, когда поток в течение какого-то времени обслуживает первый запрос, другой поток не ожидает завершения первого, он подхватывает второй запрос и начинает работать над ним. И так далее.

Таким образом, единственное время, которое вы потеряете, - это время, необходимое для чтения запроса из очереди. Это время обычно ничтожно мало по сравнению со временем обработки запроса.

2 голосов
/ 26 марта 2013

На этот вопрос нет однозначного ответа, поскольку это зависит не только от протокола FastCGI, но и, прежде всего, от используемого менеджера процессов FastCGI.Для веб-серверов Apache2 диспетчер процессов FastCGI обычно может иметь значение mod_fastcgi или mod_fcgid.Оба они ведут себя по-разному.mod_fastcgi, похоже, поддерживает многопоточность и будет отправлять параллельные запросы на сервер FastCGI, который объявил о своей поддержке.mod_fcgid пока (может измениться в будущем?) Не поддерживает многопоточность и всегда будет запускать новый процесс сервера FastCGI по параллельному запросу и никогда не будет отправлять параллельные запросы на сервер FastCGI.

Всео том, чтобы сказать: да, FastCGI имеет положение для многопоточных серверов FastCGI, но среда, в которой работает сервер FastCGI, также должна сделать эту функцию реальностью ... на практике это может или не может, и, к сожалению,mod_fcgid нет, по крайней мере, пока.

Если ваш FastCGI SDK был от mod_fcgid, это может быть причиной того, что ответ на запрос управления FCGI_MAX_CONNS всегда возвращает фиксированный фиксированный кодзначение 1.

Вас может заинтересовать мой недавний вопрос и две другие веб-ссылки, в которых все три упоминают конкретную тему многопоточного сервера FastCGI:

1 голос
/ 23 июня 2012

Я думаю, что вы можете тестировать таким образом, чтобы ограничить вас однопоточностью. Я столкнулся с подобной ситуацией, используя libfcgi и lighttpd, но решил, что если я буду использовать Firefox для тестирования, то Firefox будет искусственно ограничивать отправку HTTP-запроса на сервер, пока предыдущий запрос на тот же сервер не завершится. Инструмент, который вы используете для тестирования, может сделать что-то похожее.

Вам не нужно изменять FCGI_MAX_CONNS, FCGI_MAX_REQS или FGCI_MPXS_CONNS. Жестко закодированные значения не должны иметь значения для современных веб-серверов, таких как nginx или lighttpd.

Использование инструмента командной строки, такого как curl и одновременное создание 20 процессов curl для всех попаданий на сервер, приводит к активации всех 20 потоков и одновременному завершению всех 20 процессов curl после 2 секунд, при работе с примером threadaded.c, предоставленным SDK (который имеет явный вызов sleep(2)).

Моя конфигурация lighttpd установлена ​​так:

fastcgi.server = (
    "/test" => (
        "test.fastcgi.handler" => (
            "socket" => "/tmp/test.fastscgi.socket",
            "check-local" => "disable",
            "bin-path" => "/tmp/a.fastcgi",
            "max-procs" => 1,
        )
    )
)

max-procs при значении 1 будет появляться только одна копия вашей программы fcgi, и lighttpd должен сообщать об увеличении «нагрузки» на сокет при поступлении запросов до завершения предыдущего запроса.

Если вы породили 21 процесс скручивания, первые 20 должны завершиться через 2 секунды, затем последний должен завершиться через 2 секунды. Нерест 40 процессов скручивания должен занимать почти столько же времени, сколько 21 (всего чуть более 4 секунд).

...