Подтвердить что ты не робот

Nginx, fastcgi и открытые сокеты

Я экспериментирую с использованием fastcgi на nginx, но у меня возникли некоторые проблемы. Nginx не повторно использует соединения, он дает 0 в битах BeginRequest, поэтому приложение должно закрыть соединение после завершения запроса.

У меня есть следующий код для закрытия:

socket.shutdown(SocketShutdown.BOTH);
socket.close();

Проблема в том, что соединения фактически не закрыты. Они задерживаются как TIME_WAIT, а nginx (или что-то) не будет открывать новые соединения. Я предполагаю, что я делаю что-то не так, когда закрываю сокеты, но я не знаю, что.. На связанную ноту - как я могу заставить nginx поддерживать открытые соединения?

Это использует nginx 1.0.6 и D 2.055

EDIT: Не подошли ближе, но я также проверил опцию linger, и она выключена:

linger l;
socket.getOption(SocketOptionLevel.SOCKET, SocketOption.LINGER, l);
assert(l.on == 0); // off

getOption возвращает 4, хотя.. Не знаю, что это значит. Возвращаемое значение недокументировано.

EDIT: Я также попытался использовать TCP_NODELAY для последнего отправленного сообщения, но это тоже не имело никакого эффекта:

socket.setOption(SocketOptionLevel.SOCKET, SocketOption.TCP_NODELAY, 1);

EDIT: Поддержка nginx 1.1.4 поддерживает живые соединения. Это не работает должным образом. Правильно сообщите, что сервер отвечает за управление жизненным циклом соединения, но он по-прежнему создает новый сокет для каждого запроса.

4b9b3361

Ответ 1

Прокси-сервер NGINX keepalive

Относительно nginx (v1.1) keepalive для fastcgi. Правильный способ его настройки заключается в следующем:

upstream fcgi_backend {
  server localhost:9000;
  keepalive 32;
}

server {
  ...
  location ~ \.php$ {
    fastcgi_keep_conn on;
    fastcgi_pass fcgi_backend;
    ...
  }
}

TIME_WAIT

Состояние соединения TCP TIME_WAIT не имеет ничего общего с записями, tcp_no_delays, таймаутами и т.д. Он полностью управляется ядром ОС и на него могут влиять только параметры конфигурации всей системы. Как правило, это неизбежно. Это просто способ работы протокола TCP. Прочитайте об этом здесь и здесь.

Самый радикальный способ избежать TIME_WAIT - это reset (отправить RST пакет) TCP-соединение при закрытии, установив linger = ON и linger_timeout = 0. Но делать это таким образом не рекомендуется для нормальной работы, так как вы можете потерять неотправленные данные. Только сокет reset в условиях ошибки (таймауты и т.д.).

Я бы попробовал следующее. После отправки всех ваших данных вызовите socket.shutdown(WRITE) (это отправит пакет FIN другой стороне) и еще не закрывает сокет. Затем продолжайте чтение из гнезда, пока не получите указание о том, что соединение закрыто другим концом (в C, который обычно обозначается 0-length read()). После получения этого указания закройте сокет. Подробнее об этом здесь.

TCP_NODELAY и TCP_CORK

Если вы разрабатываете какой-либо сетевой сервер, вы должны изучить эти параметры, так как они влияют на производительность. Без их использования у вас может возникнуть задержка 20 мс (задержка Nagle) для каждого отправленного пакета. Хотя эти задержки выглядят небольшими, они могут неблагоприятно влиять на статистику ваших запросов в секунду. Хорошее чтение об этом здесь.

Еще одна полезная ссылка для чтения о сокетах - здесь.

О FastCGI

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

Ответ 2

Привет, simendsjo,

Следующие предложения могут быть полностью недоступны.

Я использую NginX для разработки; однако Im абсолютно не эксперт по NginX.

Работники NginX

Тем не менее, ваша проблема вернула в память что-то о рабочих и рабочих процессах в NginX.

Кроме того, рабочие и рабочие процессы в NginX используются для уменьшения латентности, когда рабочие блокируют на дисковый ввод-вывод и ограничивают количество подключений на процесс при выборе select()/poll().

Вы можете найти дополнительную информацию здесь.

NginX Fcgiwrap и сокет

Другим указателем может быть следующий код, хотя этот пример специфичен для Debian.

#!/usr/bin/perl

use strict;
use warnings FATAL => qw( all );

use IO::Socket::UNIX;

my $bin_path = '/usr/local/bin/fcgiwrap';
my $socket_path = $ARGV[0] || '/tmp/cgi.sock';
my $num_children = $ARGV[1] || 1;

close STDIN;

unlink $socket_path;
my $socket = IO::Socket::UNIX->new(
    Local => $socket_path,
    Listen => 100,
);

die "Cannot create socket at $socket_path: $!\n" unless $socket;

for (1 .. $num_children) {
    my $pid = fork;
    die "Cannot fork: $!" unless defined $pid;
    next if $pid;

    exec $bin_path;
    die "Failed to exec $bin_path: $!\n";
}

Дополнительную информацию об этом решении можно найти здесь.

Ответ 3

Установите тайм-аут сокета как можно ниже, а затем закройте сокет. Если после этого вы попытаетесь написать что-нибудь в сокет, что произойдет? Можно нажать неэкранированный двоичный код, чтобы сигнализировать о тесном соединении, заставляя его закончить. То, как IE стал известен как Internet Exploder