многопоточные приложения FastCGI-сервера

Я хочу написать приложение FastCGI, которое должно обрабатывать несколько одновременных запросов с использованием потоков. Я взглянул на резьбой.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. Так что резьба.c образец никогда не получит несколько соединений.

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

3 ответов


протестировал резьбу.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, когда поток обслуживая первый запрос в течение любого времени, другой поток не ждет завершения первого, он подберет второй запрос и начнет работать над ним. И так далее.

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


на этот вопрос нет единого ответа, так как это зависит не только от протокола 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:


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

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

С помощью инструмента командной строки, как curl и нерест 20 процессов скручиваемости сразу, чтобы все попали на сервер, приводит к активации всех 20 потоков и завершению всех 20 процессов скручиваемости одновременно, через 2 секунды, при запуске против примера с резьбой.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 секунд).